0

I just realized that the comments are broken on a Wordpress site I'm working on (LEMP+memcached), and can't figure out why. I'm sure it's not related to my theme nor any plugins. Basically, anyone tries to submit a comment, nginx gets stuck on the wp-comments-post.php with an HTTP 405 error instead of fulfilling the POST request.

From what I can tell, the issue appears to be how nginx handles a POST request to wp-comments-post.php, where it returns an HTTP 405 instead of redirecting it correctly.

I had a similar issue here with doing a POST request on an email submission plugin, and that was fixed by telling memcached to redirect the 405 error. Memcached should be passing 405s back to nginx, but I'm not sure how nginx and php-fpm handle errors from there (especially with fastcgi caching being used).

Here is my nginx.conf:

user www-data;
worker_processes 4;
pid /run/nginx.pid;

events {
    worker_connections 4096;
    multi_accept on;
    use epoll;
}

http {

##
# Basic Settings
##

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 15;
keepalive_requests 65536;
client_body_timeout 12;
client_header_timeout 15;
send_timeout 15;
types_hash_max_size 2048;
server_tokens off;

server_names_hash_max_size 1024;
server_names_hash_bucket_size 1024;

include /etc/nginx/mime.types;  

index index.php index.html index.htm;

client_body_temp_path /tmp/client_body;
proxy_temp_path /tmp/proxy;
fastcgi_temp_path /tmp/fastcgi;
uwsgi_temp_path /tmp/uwsgi;
scgi_temp_path /tmp/scgi;   
fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=phpcache:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
default_type application/octet-stream;

client_body_buffer_size 16K;
client_header_buffer_size 1K;
client_max_body_size 8m;
large_client_header_buffers 2 1k;

##
# Logging Settings
##

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

##
# Gzip Settings
##

gzip on;
gzip_disable "msie6";
gzip_min_length 1000;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 2;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json image/svg+xml image/png image/gif image/jpeg application/x-javascript text/xml application/xml application/xml+rss text/javascript font/ttf font/otf font/eot x-font/woff application/x-font-ttf application/x-font-truetype application/x-font-opentype application/font-woff application/font-woff2 application/vnd.ms-fontobject audio/mpeg3 audio/x-mpeg-3 audio/ogg audio/flac audio/mpeg application/mpeg application/mpeg3 application/ogg;

etag off;

##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

upstream php {
    server unix:/var/run/php/php7.0-fpm.sock;
}

server {    
    listen 80; # IPv4
    listen [::]:80; # IPv6
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    server_name example.com www.example.com;
    listen 443 default http2 ssl; # SSL
    listen [::]:443 default http2 ssl; # IPv6
    ssl on;
    ssl_certificate /etc/nginx/ssl/tls.crt;
    ssl_certificate_key /etc/nginx/ssl/priv.key;
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 24h;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES256+EECDH:AES256+EDH:!aNULL;
    ssl_protocols TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_stapling on;
    ssl_stapling_verify on;

    add_header Public-Key-Pins 'pin-sha256="...; max-age=63072000; includeSubDomains;';
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    add_header X-Content-Type-Options "nosniff";
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Dns-Prefetch-Control 'content=on';

    root /home/user/selfhost/html;
    include /etc/nginx/includes/*.conf; # Extra config

    client_max_body_size 10M;

    location / {
        set $memcached_key "$uri?$args";
        memcached_pass  127.0.0.1:11211;
        error_page 404 403 405 502 504 = @fallback;     
        expires 86400;

            location ~ \.(css|ico|jpg|jpeg|js|otf|png|ttf|woff) {
                                set $memcached_key "$uri?$args";
                                memcached_pass 127.0.0.1:11211;
                                error_page 404 502 504 = @fallback;
                                #expires epoch;
                    }

    }

    location @fallback {
        try_files $uri $uri/ /index.php$args;
        #root /home/user/selfhost/html;
        if ($http_origin ~* (https?://[^/]*\.example\.com(:[0-9]+)?)) {
                    add_header 'Access-Control-Allow-Origin' "$http_origin";
                    }
        if (-f $document_root/maintenance.html) {
            return 503;
        }

    }
    location ~ [^/]\.php(/|$) {
        # set cgi.fix_pathinfo = 0; in php.ini
        include proxy_params;
        include fastcgi_params;
        #fastcgi_intercept_errors off;
        #fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
        fastcgi_pass php;
        fastcgi_cache phpcache;
        fastcgi_cache_valid 200 60m;
        #error_page 404 405 502 504 = @fallback;
    }

    location ~ /nginx.conf {
        deny all;
    }

            location /nginx_status {
                    stub_status on;
                    #access_log off;
        allow 159.203.18.101;
                    allow 127.0.0.1/32;
        allow 2604:a880:cad:d0::16d2:d001;
                    deny all;
            }


    location ^~ /09qsapdglnv4eqxusgvb {
        auth_basic "Authorization Required";
        auth_basic_user_file htpass/adminer;
        #include fastcgi_params;

                location ~ [^/]\.php(/|$) {
                        # set cgi.fix_pathinfo = 0; in php.ini
                        #include fastcgi_params;
            include fastcgi_params;
                        #fastcgi_intercept_errors off;
                        #fastcgi_pass unix:/var/run/php7.0-fpm.sock;
                        fastcgi_pass php;
                        fastcgi_cache phpcache;
                        fastcgi_cache_valid 200 60m;
                }



    }


    error_page 503 @maintenance;
    location @maintenance {
        rewrite ^(.*)$ /.maintenance.html break;
    }

}

And here is fastcgi_params:

fastcgi_param   SCRIPT_FILENAME     $document_root$fastcgi_script_name;
fastcgi_param   QUERY_STRING        $query_string;
fastcgi_param   REQUEST_METHOD      $request_method;
fastcgi_param   CONTENT_TYPE        $content_type;
fastcgi_param   CONTENT_LENGTH      $content_length;

#fastcgi_param  SCRIPT_FILENAME     $request_filename;
fastcgi_param   SCRIPT_NAME         $fastcgi_script_name;
fastcgi_param   REQUEST_URI         $request_uri;
fastcgi_param   DOCUMENT_URI        $document_uri;
fastcgi_param   DOCUMENT_ROOT       $document_root;
fastcgi_param   SERVER_PROTOCOL     $server_protocol;

fastcgi_param   GATEWAY_INTERFACE   CGI/1.1;
fastcgi_param   SERVER_SOFTWARE     nginx/$nginx_version;

fastcgi_param   REMOTE_ADDR     $remote_addr;
fastcgi_param   REMOTE_PORT     $remote_port;
fastcgi_param   SERVER_ADDR     $server_addr;
fastcgi_param   SERVER_PORT     $server_port;
fastcgi_param   SERVER_NAME     $server_name;

fastcgi_param   HTTPS           $https if_not_empty;

fastcgi_param AUTH_USER $remote_user;
fastcgi_param REMOTE_USER $remote_user;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param   REDIRECT_STATUS     200;

fastcgi_param   PATH_INFO       $fastcgi_path_info;

fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;
fastcgi_max_temp_file_size 0;
fastcgi_index index.php;

fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_keep_conn on;

Here are request logs:

xxx.xxx.xxx.xxx - - [26/Apr/2017:00:11:59 +0000] "GET /2016/12/31/hello-world/ HTTP/2.0" 200 9372 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/57.0.2987.98 Chrome/57.0.2987.98 Safari/537.36"
xxx.xxx.xxx.xxx - - [26/Apr/2017:00:12:01 +0000] "POST /wp-comments-post.php HTTP/2.0" 405 626 "https://example.com/2016/12/31/hello-world/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/57.0.2987.98 Chrome/57.0.2987.98 Safari/537.36"
xxx.xxx.xxx.xxx - - [26/Apr/2017:00:12:01 +0000] "GET /favicon.ico HTTP/2.0" 200 571 "https://example.com/wp-comments-post.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/57.0.2987.98 Chrome/57.0.2987.98 Safari/537.36"
xxx.xxx.xxx.xxx - - [26/Apr/2017:00:21:20 +0000] "POST /wp-comments-post.php HTTP/2.0" 405 626 "https://example.com/2016/12/31/hello-world/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/57.0.2987.98 Chrome/57.0.2987.98 Safari/537.36"
xxx.xxx.xxx.xxx - - [26/Apr/2017:00:21:21 +0000] "GET /favicon.ico HTTP/2.0" 200 571 "https://example.com/wp-comments-post.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/57.0.2987.98 Chrome/57.0.2987.98 Safari/537.36"
xxx.xxx.xxx.xxx - - [26/Apr/2017:00:24:07 +0000] "POST /wp-comments-post.php HTTP/2.0" 405 626 "https://example.com/2016/12/31/hello-world/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/57.0.2987.98 Chrome/57.0.2987.98 Safari/537.36"
xxx.xxx.xxx.xxx - - [26/Apr/2017:00:24:07 +0000] "GET /favicon.ico HTTP/2.0" 200 571 "https://example.com/wp-comments-post.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/57.0.2987.98 Chrome/57.0.2987.98 Safari/537.36"
  • This sounds like the kind of question where configuration, logs, and a demonstration of the problem could be useful. – Tim Apr 26 '17 at 0:40
  • Sorry, accidentally posted before adding the config. I'll grab some logs too. – 4oo4 Apr 26 '17 at 0:46
  • Added logs, let me know if you want anything else – 4oo4 Apr 26 '17 at 0:55
  • Rather than dumping logs I suggest you do a single "curl" (or request) that demonstrates the problem, showing just the relevant log entries in both memcached and nginx logs. In both cases check access and error logs. – Tim Apr 26 '17 at 1:12
  • 1
    Implementing caching in nginx without deep understanding of every aspect of WordPress is a recipe for disaster. There are many cases where you are caching the wrong information, which breaks things. Also, new WordPress versions might break your caching setup in new ways. My recommendation is to use the standard WordPress caching mechanisms. – Tero Kilkanen Apr 26 '17 at 10:54
0

It actually wasn't anything to do with caching like I suspected, it turned out the culprit was in some nginx config that comes with iThemes Security. When I disabled the plugin and totally forgot that it includes its own nginx config.

I'm an idiot for not digging into my nginx config enough, but thanks to everyone for suggestions. Specifically, this was what caused it:

location = /wp-comments-post.php {
    limit_except POST { deny all; }
    if ($http_user_agent ~ "^$") { return 403; }
    valid_referers server_names jetpack.wordpress.com/jetpack-comment/;
    if ($invalid_referer) { return 403; }
}

When I added $server_name to the valid_referrers it that fixed it, so I am going to disable this part of the plugin and use custom config. Something in there is causing it, just need to mess with it to figure out what.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.