Security vulnerabilities are often exploits of software that fails when trying to deal with unexpected input. Other times they are exploits of a misconfiguration or a service that unintentionally was open to the public.

For the above reasons, we should limit as much as possible what services are exposed to the public and limit as much as possible what they do and accept from the visitors. To follow those security principles, we should only allow the HTTP methods for which we, in fact, provide services. Under all normal circumstances, that would be the methods GET, POST and HEAD.

Add the following lines to your configuration, either in your server block to make the restriction global to your website, or in a location block to restrict only that part of your website.


Update June 14, 2016:

Skip the rest of this article. The method described here is not the best way to implement this. As sayetan points out in a comment: There is a limit_except directive you should use instead.


Whitelist allowed HTTP methods

add_header Allow "GET, POST, HEAD" always;
if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
	return 405;
}

This configuration will return the HTTP status 405 Method Not Allowed for all requests that try a method not specified in our whitelist.

[bjornad]

The HTTP spec states we MUST include an Allow header when sending the 405 response code, so we do that for all requests. Technically, we only need to send the Allow header on the 405 responses, but if you know a way to use the add_header statement within an if, please let me know.


Update December 15, 2015: Christian Bricart reached out to me on Twitter, with an approach which let you set the Allow header only on 405-responses:

error_page 405 @error405;
location @error405 {
	add_header Allow "GET, POST, HEAD" always;
}

Different allowed HTTP methods for different request URIs

Note that if you are providing an API that use other request methods – like the WordPress REST API – you need a much more advanced configuration to allow certain request methods, e.g. PUT and DELETE, to specific endpoints. If you’re not 100% sure what you are doing – or getting yourself into - you better drop it.

To have a match on specific request URIs and request methods without using location blocks, we have to overcome the limitation in Nginx configuration that it supports neither nested if s nor any type of the OR or AND conditional operators. You can do that by using maps and a “clever hack” in the configuration, similar to what I show in the post Restrict access to the WordPress dashboard by IP address in Nginx. I strongly suggest you use maps for at least the request URIs.