Skip to content

405 Method Not Allowed

What is HTTP 405 Method Not Allowed?

405 Method Not Allowed
You go to the library and find a book you want (yay, the book exists!). You try ...
HTTP 405 Method Not Allowed status code illustration

Explain Like I’m 3

Imagine you have a coloring book and crayons. Coloring IN the book is okay! But if you try to color ON the wall with the same crayon, Mom says “No! You can’t use the crayon that way on the wall!” The crayon works fine, and the wall is there, but you’re trying to use the crayon in the wrong way for the wall. That’s what 405 means - you found the right place, but you’re trying to do something that’s not allowed there!

Example: Trying to write with a marker on a dry-erase board, but the marker is permanent! The board exists and the marker works, but that’s not the right kind of marker to use on that board.

Explain Like I’m 5

You go to the library and find a book you want (yay, the book exists!). You try to write notes in the book with a pencil, but the librarian says “405 Method Not Allowed! You can READ books here, but you cannot WRITE in them.” The book is there, you found it correctly, but writing in library books isn’t allowed - you can only read them. That’s 405 Method Not Allowed - you’re trying to do the wrong action on something that exists!

Example: Trying to make a withdrawal at the bank’s deposit-only ATM. The ATM is working and you found it, but it only accepts deposits - you can’t withdraw money from it!

Jr. Developer

405 Method Not Allowed means the server recognizes the HTTP method you used (GET, POST, PUT, DELETE, etc.), but that specific endpoint doesn’t support that method. The resource exists (otherwise you’d get 404), but you’re using the wrong HTTP verb.

Common scenarios:

  • Sending POST to a read-only endpoint that only supports GET
  • Trying to DELETE a resource that doesn’t allow deletion
  • Using PUT when the endpoint only accepts PATCH
  • Sending GET to an endpoint that only accepts POST (form submission)

Key distinctions:

  • 404 Not Found: The resource/endpoint doesn’t exist at all
  • 405 Method Not Allowed: The resource exists but doesn’t support your HTTP method
  • 501 Not Implemented: The server doesn’t recognize/support that HTTP method anywhere

Important requirement: RFC 9110 requires the server to include an Allow header listing the supported methods:

HTTP/1.1 405 Method Not Allowed
Allow: GET, POST, HEAD

This tells the client which methods ARE allowed on this resource.

Code Example

// Express.js: Endpoint that only allows GET and POST
const express = require('express');
const app = express();
// Correct way: explicitly handle allowed methods
app.route('/api/users/:id')
.get((req, res) => {
// GET is allowed
res.json({ id: req.params.id, name: 'John' });
})
.post((req, res) => {
// POST is allowed
res.status(201).json({ message: 'User created' });
})
.all((req, res) => {
// All other methods (PUT, DELETE, PATCH, etc.) return 405
res.status(405)
.set('Allow', 'GET, POST')
.json({
error: 'Method Not Allowed',
message: `${req.method} is not supported on this endpoint`,
allowed_methods: ['GET', 'POST']
});
});
// Read-only endpoint example
app.route('/api/config')
.get((req, res) => {
res.json({ theme: 'dark', language: 'en' });
})
.all((req, res) => {
// Config is read-only, only GET allowed
res.status(405)
.set('Allow', 'GET, HEAD')
.json({
error: 'Method Not Allowed',
message: 'Configuration is read-only',
allowed_methods: ['GET', 'HEAD']
});
});
// Middleware to log 405 errors
app.use((req, res, next) => {
const originalSend = res.send;
res.send = function(data) {
if (res.statusCode === 405) {
console.warn(`405 Method Not Allowed: ${req.method} ${req.path}`);
}
originalSend.call(this, data);
};
next();
});

Crash Course

HTTP 405 Method Not Allowed (RFC 9110 §15.5.6) indicates the server knows the HTTP method but the target resource doesn’t support it. This differs from 501 Not Implemented (method unknown to server entirely) and 404 Not Found (resource doesn’t exist).

RFC 9110 Definition: “The 405 (Method Not Allowed) status code indicates that the method received in the request-line is known by the origin server but not supported by the target resource. The origin server MUST generate an Allow header field in a 405 response containing a list of the target resource’s currently supported methods.”

Decision Tree:

  1. Does the resource exist? → No → 404 Not Found
  2. Does the server understand the method? → No → 501 Not Implemented
  3. Does this resource support the method? → No → 405 Method Not Allowed
  4. Does user have permission? → No → 403 Forbidden
  5. Everything OK → 200/201/204 Success

Common REST API Scenarios:

  1. Read-Only Resources

    • GET /api/system/health → 200 OK
    • POST /api/system/health → 405 (read-only endpoint)
    • Allow: GET, HEAD
  2. Write-Only Resources

    • POST /api/events/log → 201 Created
    • GET /api/events/log → 405 (write-only endpoint)
    • Allow: POST
  3. Partial CRUD Implementation

    • GET /api/articles/:id → 200 OK
    • PUT /api/articles/:id → 200 OK
    • DELETE /api/articles/:id → 405 (deletion not allowed)
    • Allow: GET, HEAD, PUT, PATCH
  4. Method Aliases

    • PATCH /api/users/:id → 200 OK
    • PUT /api/users/:id → 405 (only PATCH supported for partial updates)
    • Allow: GET, PATCH, DELETE

Allow Header Specification:

The Allow header must list supported methods:

  • Simple: Allow: GET, POST
  • Comprehensive: Allow: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS
  • Empty (rare): Allow: (resource temporarily allows no methods)

CORS and OPTIONS:

OPTIONS requests often bypass 405 for CORS preflight:

  • Browser sends OPTIONS before actual request
  • Server responds with allowed methods in…

Code Example

// Comprehensive 405 handling with Express.js middleware
const express = require('express');
const app = express();
// Middleware to track allowed methods per route
const methodRegistry = new Map();
function registerMethods(path, methods) {
methodRegistry.set(path, methods);
}
// Method enforcement middleware
function enforceMethod(...allowedMethods) {
return (req, res, next) => {
if (allowedMethods.includes(req.method)) {
return next();
}
// Method not allowed
const allow = [...allowedMethods, 'OPTIONS'].join(', ');
res.status(405)
.set('Allow', allow)
.json({
error: 'Method Not Allowed',
message: `${req.method} is not supported for this resource`,
method_used: req.method,
allowed_methods: allowedMethods,
resource: req.path
});
};
}
// Read-only resource
app.get('/api/system/version',
enforceMethod('GET'),
(req, res) => {
res.json({ version: '1.2.3', build: '2024-01-05' });
}
);
registerMethods('/api/system/version', ['GET', 'HEAD']);
// Full CRUD resource
const userMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
app.route('/api/users/:id')
.get(
enforceMethod('GET'),
async (req, res) => {
const user = await db.users.findById(req.params.id);
res.json(user);
}
)
.put(
enforceMethod('PUT'),
async (req, res) => {
const user = await db.users.update(req.params.id, req.body);
res.json(user);
}
)
.delete(
enforceMethod('DELETE'),
async (req, res) => {
await db.users.delete(req.params.id);
res.status(204).end();
}
)
.all((req, res) => {
const allow = 'GET, PUT, DELETE, OPTIONS';
res.status(405)
.set('Allow', allow)
.json({
error: 'Method Not Allowed',
message: `${req.method} is not supported`,
allowed_methods: ['GET', 'PUT', 'DELETE']
});
});
registerMethods('/api/users/:id', ['GET', 'PUT', 'DELETE']);
// CORS preflight handling
app.options('*', (req, res) => {
const path = req.path;
const methods = methodRegistry.get(path) || ['GET', 'OPTIONS'];
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Methods', methods.join(', '));
res.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.set('Allow', methods.join(', '));
res.status(204).end();
});
// Global 405 handler for unmatched routes
app.use((req, res, next) => {
// Check if route exists for a...

Deep Dive

HTTP 405 Method Not Allowed represents a fundamental constraint in REST architecture: resources have defined affordances (allowed operations). Understanding 405 requires examining HTTP method semantics, the Allow header requirement, and the relationship to other 4xx/5xx codes.

RFC 9110 Specification (Section 15.5.6):

“The 405 (Method Not Allowed) status code indicates that the method received in the request-line is known by the origin server but not supported by the target resource. The origin server MUST generate an Allow header field in a 405 response containing a list of the target resource’s currently supported methods.”

Key word: MUST. The Allow header is not optional - servers violating this are non-compliant.

HTTP Method Semantics:

HTTP defines method semantics in RFC 9110 §9:

  • Safe methods (GET, HEAD, OPTIONS): Read-only, no side effects
  • Idempotent methods (GET, HEAD, PUT, DELETE, OPTIONS): Same result if called multiple times
  • Cacheable methods (GET, HEAD, POST with explicit caching)

A 405 response indicates the resource’s state machine doesn’t support transitioning via the requested method.

405 vs 501 vs 404 - Semantic Precision:

  • 404 Not Found: “I don’t know about /api/unicorns”

    • Resource doesn’t exist in URI space
    • May exist in future or at different location
    • Client may have wrong URL
  • 405 Method Not Allowed: “I know /api/users/123, but it doesn’t support DELETE”

    • Resource exists and is identified
    • Server understands the HTTP method
    • This specific resource prohibits that method
    • Client should use different method
  • 501 Not Implemented: “I don’t understand WEBDAV methods like PROPFIND”

    • Server doesn’t recognize/support the method at all
    • Applies globally, not to specific resource
    • Rarely used (servers typically support standard methods)

The Allow Header:

RFC 9110 §10.2.1 defines Allow as: “The Allow header field lists the set of methods advertised as supported by the target resource.”

Syntax: Allow: Method [, Method]*

Examples:

  • Allow: GET, HEAD, OPTIONS (read-only)
  • Allow: GET, POST, PUT, DELETE, OPTIONS (full CRUD)
  • Allow: (empty - resource temporarily allows no methods)

Empty Allow header is valid but rare - indicates resource exists but no operations allowed (perhaps during maintenance).

CORS Interaction:

CORS preflight requests (OPTIONS) create complexity:

  1. Browser sends OPTIONS to check permissions
  2. Server responds with Ac…

Code Example

// Production-grade 405 handling with dynamic method detection
const express = require('express');
const app = express();
// Advanced method registry with metadata
class MethodRegistry {
constructor() {
this.routes = new Map();
}
register(path, method, metadata = {}) {
if (!this.routes.has(path)) {
this.routes.set(path, new Map());
}
this.routes.get(path).set(method, metadata);
}
getMethods(path) {
const route = this.routes.get(path);
return route ? Array.from(route.keys()) : [];
}
supports(path, method) {
const route = this.routes.get(path);
return route ? route.has(method) : false;
}
getAllowHeader(path) {
const methods = this.getMethods(path);
// Always include OPTIONS for CORS
if (methods.length > 0 && !methods.includes('OPTIONS')) {
methods.push('OPTIONS');
}
// HEAD must be supported if GET is supported (RFC 9110)
if (methods.includes('GET') && !methods.includes('HEAD')) {
methods.push('HEAD');
}
return methods.sort().join(', ');
}
}
const registry = new MethodRegistry();
// Decorator to auto-register methods
function httpMethod(method) {
return function(target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
const [req, res] = args;
// Register this method for this path
registry.register(req.route.path, method, {
handler: propertyKey,
timestamp: new Date()
});
return originalMethod.apply(this, args);
};
return descriptor;
};
}
// Middleware: Automatic 405 handling
function autoHandle405(req, res, next) {
// Store original route path for registry lookup
req.routePath = req.route ? req.route.path : req.path;
const originalSend = res.send.bind(res);
res.send = function(data) {
// If no handler matched, check if route exists
if (!res.headersSent && res.statusCode === 404) {
const methods = registry.getMethods(req.routePath);
if (methods.length > 0) {
// Route exists but method not supported
res.status(405);
res.set('Allow', registry.getAllowHeader(req.routePath));
return originalSend(JSON.stringify({
error: 'Method Not Allowed',
message: `${req.method} is not supported for this resource`,
method: req.method,
allowed_methods: methods,
resource: req.routePath,
documentation: `https://api.example.com/docs${req.routePath}`
}));
}
}
return originalSend(data);
};
next();
}
app.use(autoHandle405);
// Resource controller with conditional method support
class ArticleController {
async handleRequest(req, res) {
const article = await db.articles.findById(req.params.id);
if (!article) {
return res.status(404).json({ error: 'Article not found' });
}
// Dynamic method availability based on resource state
const allowed...

Frequently Asked Questions

What's the difference between 405 Method Not Allowed and 404 Not Found?

404 means the resource/endpoint doesn't exist at all - the URL is wrong or the resource was deleted. 405 means the resource exists and you found the correct URL, but you're using the wrong HTTP method (like using POST when only GET is allowed). If you get 405, try a different HTTP method; if you get 404, check the URL.

What's the difference between 405 Method Not Allowed and 501 Not Implemented?

501 means the server doesn't recognize or support that HTTP method anywhere in the entire application (e.g., server doesn't support PROPFIND WebDAV method). 405 means the server understands the method (like DELETE) but this specific resource doesn't support it. 501 is global server limitation, 405 is resource-specific restriction.

Why do I get 405 when the Allow header says my method is allowed?

This can happen with CORS preflight requests. The Allow header shows what the resource supports, but CORS may block it for cross-origin requests. Check Access-Control-Allow-Methods header and ensure your request includes proper CORS headers. Also, some frameworks show allowed methods for the route in general, not considering resource state.

Should servers return 405 or 403 for permission-based method restrictions?

Use 403 Forbidden for permission/authorization issues (user lacks permission to delete), and 405 for capability issues (resource doesn't support delete operation at all). 405 = 'this resource cannot be deleted by anyone', 403 = 'you specifically are not allowed to delete this'. Use 405 for resource design, 403 for access control.

Is the Allow header required in 405 responses?

Yes, RFC 9110 uses the word 'MUST' - the Allow header is mandatory in 405 responses. It should list all HTTP methods the resource supports (e.g., 'Allow: GET, POST, OPTIONS'). Servers that omit this header are non-compliant. The header helps clients know what methods they can use instead.

Why do I get 405 for HEAD requests when GET works?

This is a server bug. RFC 9110 requires that if a resource supports GET, it must also support HEAD (same response but without body). Servers returning 405 for HEAD when GET is allowed are violating the HTTP specification. Report this to the API maintainers.

How should I handle 405 errors in my API client?

Don't retry with the same method - it won't work. Check the Allow header to see supported methods. If your use case requires a different method (e.g., you need DELETE but only GET is allowed), this is an API design limitation you'll need to work around or discuss with the API provider. Update your code to use an allowed method if possible.

Common Causes

  • Using POST to a read-only endpoint that only supports GET
  • Trying to DELETE a resource that doesn’t allow deletion
  • Using GET on a write-only endpoint that only accepts POST
  • Using PUT when the API only supports PATCH for updates
  • Trying to modify a read-only configuration endpoint
  • Using deprecated HTTP methods on modern APIs
  • CORS preflight issues (OPTIONS not properly handled)
  • API version differences (older version has fewer supported methods)
  • Resource state restrictions (e.g., published articles can’t be edited)
  • Framework routing issues (method not registered for route)
  • Load balancer or proxy configuration blocking certain methods
  • Typo in HTTP method name (e.g., ‘DELET’ instead of ‘DELETE’)

Implementation Guidance

  • Check the Allow header in the response to see which methods are supported
  • Review API documentation for correct HTTP method for this endpoint
  • Use GET for reading data, POST for creating, PUT/PATCH for updating, DELETE for removing
  • If you need a method that’s not allowed, check if there’s an alternative endpoint
  • Verify you’re using the correct API version (newer versions may support more methods)
  • For CORS issues: ensure OPTIONS requests are handled and return proper headers
  • Check if resource state affects allowed methods (draft vs published, etc.)
  • Verify authentication - some methods may require higher permissions (but would be 403, not 405)
  • If building API: implement missing HTTP methods if they make sense for the resource
  • If building API: ensure HEAD is supported when GET is supported (RFC requirement)
  • Check proxy/load balancer configuration if certain methods are blocked
  • Use API testing tools (Postman, curl) to verify which methods actually work

Comments