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"
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:
When I addedSomething in there is causing it, just need to mess with it to figure out what.$server_name
to thevalid_referrers it
that fixed it, so I am going to disable this part of the plugin and use custom config.