Skip to content
Web security Reviewed 2026-09-12

Unsafe HTTP Method

What does this mean ?

In HTTP terminology, a safe method has read-only semantics: the client is not asking for a state change. GET, HEAD and OPTIONS are examples. POST, PUT, PATCH and DELETE legitimately perform changes. Supporting both GET and POST is not, by itself, a vulnerability.

A problem occurs when an operation accepts methods that bypass its security checks, when a GET performs a sensitive change, or when route method restrictions are mistaken for authorization.

What can happen ?

A state-changing GET can be triggered by navigation, prefetching or another website. Inconsistent checks across methods can let a caller perform an operation through an unexpected route. The same authorization policy must apply to every path that reaches the operation.

Recommendation

Declare the methods each route supports. Return a proper 405 Method Not Allowed response with an Allow header when a known resource receives an unsupported method. Keep GET/HEAD free of requested business-state changes. Logging and metrics are incidental server side effects; they do not make a read endpoint a write operation.

Authenticate and authorize writes, validate their input, and apply CSRF protection when credentials are attached automatically by the browser. Review framework method-override headers or fields and middleware ordering. Do not remove legitimate POST functionality simply to make a scanner finding disappear.

Sample Code

These snippets show route restrictions, not a complete security policy. The update handlers or global middleware must enforce authentication, authorization and the applicable CSRF controls before mutating data.

from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST
from django.http import HttpResponse

@login_required
@require_POST
def update_preferences(request):
    # Retain Django CSRF middleware; validate and authorize fields here.
    return HttpResponse(status=204)

Express, where the three middleware functions are the application's reviewed implementations:

app.post('/preferences', authenticate, authorizePreferences, protectCsrf, updatePreferences);
app.all('/preferences', (req, res) => {
  res.set('Allow', 'POST').sendStatus(405);
});

Register this fallback after the POST handler. Provide a separate OPTIONS/preflight route if this resource is intentionally used cross-origin. The names alone are not implementations of those controls.

ASP.NET Core MVC:

[Microsoft.AspNetCore.Authorization.Authorize]
[Microsoft.AspNetCore.Mvc.HttpPost("/preferences")]
[Microsoft.AspNetCore.Mvc.ValidateAntiForgeryToken]
public Microsoft.AspNetCore.Mvc.IActionResult UpdatePreferences()
{
    // Validate fields and authorize the particular account's update.
    return NoContent();
}

This method belongs to a controller with an established authentication scheme. HttpPost alone does not establish that scheme or record-level access.

Spring MVC with the security filter chain applied:

@org.springframework.web.bind.annotation.PostMapping("/preferences")
org.springframework.http.ResponseEntity<Void> updatePreferences() {
    // Authenticate/authorize and validate input before updating.
    return org.springframework.http.ResponseEntity.noContent().build();
}

Avoid an unrestricted @RequestMapping for a state-changing action.

A method gate for an existing authorized handler:

func postOnly(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if r.Method != http.MethodPost {
            w.Header().Set("Allow", http.MethodPost)
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
            return
        }
        next.ServeHTTP(w, r)
    })
}

Import net/http. Mount the gate around the real authentication, authorization and CSRF chain. Go 1.22+ ServeMux method patterns can also restrict routes directly.

Regression checks

For each write route, test the permitted method with valid credentials and invalid credentials separately. Verify GET, HEAD and unexpected methods cannot mutate state. Check the response status and Allow header. Exercise any method-override feature to ensure it cannot skip middleware. For cookie-authenticated writes, test missing and invalid CSRF tokens independently of the method gate.

References