I installed a LEMP stack on an Amazon ec2 server. I'm not familiar with nginx - this is purposely a learning experience for me. Unfortunately I messed something up somewhere along the way, and certain files are giving 403 errors, and I can't figure out why.

I also installed wordpress in a /blog/ subdirectory, and for a while I thought the problem was limited to the /blog/ folder, but that doesn't seem to be the case.

Many other files are good, such as .php files etc. Some image files are good, some aren't. I have a feeling it comes down to nginx's location blocks - I'm still getting used to them and I just can't find what I'm missing.

Here are some examples of problem files:

  • /include/fonts/FugazOne-Regular.ttf
  • /blog/wp-content/uploads/2017/03/cheetah1th-300x200.jpg
  • /blog/wp-content/themes/twentyseventeen/assets/js/jquery.scrollTo.js?ver=2.1.2

Meanwhile, here are some files that work just fine:

  • /index.php
  • /include/css/style.css
  • /blog/index.php
  • /images/banner.jpg
  • /images/blog/Botswana2015/IMG_2521.jpg

As I said, I'm not really familiar with nginx so I got to this point by following lots of tutorials to accomplish the following:

  • install the LEMP stack
  • set up an SSL with let's encrypt
  • install wordpress

I've read that permissions / ownership are often the problem, but that doesn't seem to be the case for me. For example:

  • most files are owned by ec2-user / ec2-user
  • some files that return 403, such as the .ttf font, have permissions set to -rw-rw-r-- which is an exact match for the stylesheet that works fine. The parent folders for each also both have the same ownership / permissions: drwxrwxr-x ec2-user ec2-user
  • the entire /blog/ directory is owned by nginx/nginx, as that's what the wordpress installation tutorial I followed told me to do..

Last, here are the various config files. Can anyone see what I'm missing?


user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    server_names_hash_bucket_size 64;

    client_max_body_size 2M;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    include /etc/nginx/conf.d/*.conf;

    include /etc/nginx/sites-enabled/*;


server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://$host$request_uri;

server {
    listen       443 ssl default_server;
    server_name  example.com;

    include /etc/nginx/sites-available/includes/restrictions.conf;
    include /etc/nginx/sites-available/includes/wordpress.conf;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location /.well-known/acme-challenge {
        root /usr/share/nginx/sites/example.com/htdocs/letsencrypt/wordpress/;

    client_max_body_size 2M;

    root   /usr/share/nginx/sites/example.com/htdocs;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/  =404;
        include /etc/nginx/sites-available/includes/php.conf;

    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;

    location = /50x.html {
        root /usr/share/nginx/sites/example.com/htdocs;


location = /favicon.ico {
    log_not_found off;
    access_log off;

location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;

location ~ /\. {
    deny all;

location ~ ^/wp-content/uploads/.*\.php$ {
    deny all;

location ~* /(?:uploads|files)/.*\.php$ {
    deny all;


location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    expires max;
    log_not_found off;

location ^~  /blog {
    root /usr/share/nginx/sites/example.com/htdocs;
    index index.php index.html index.htm;
    include /etc/nginx/sites-available/includes/php.conf;
    rewrite /wp-admin$ $scheme://$host$uri/index.php?q=$1 permanent;
    try_files $uri $uri/ @blog;

location @blog {
    rewrite ^/blog(.*) /blog/index.php?q=$1;


fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;

include fastcgi.conf;
fastcgi_intercept_errors on;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;

Your Answer

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

Browse other questions tagged or ask your own question.