Skip to content

Instantly share code, notes, and snippets.

@refringe
Last active February 5, 2024 07:50
Show Gist options
  • Star 59 You must be signed in to star a gist
  • Fork 15 You must be signed in to fork a gist
  • Save refringe/6545132 to your computer and use it in GitHub Desktop.
Save refringe/6545132 to your computer and use it in GitHub Desktop.
Nginx configuration file example for Sendy (http://sendy.co/).
server {
listen 80;
listen [::]:80;
server_name domain.com;
autoindex off;
index index.php index.html;
root /srv/www/domain.com/public;
access_log /srv/www/domain.com/logs/access.log;
error_log /srv/www/domain.com/logs/error.log;
location / {
try_files $uri $uri/ $uri.php?$args;
}
location /l/ {
rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
}
location /t/ {
rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
}
location /w/ {
rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
}
location /unsubscribe/ {
rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
}
location /subscribe/ {
rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
access_log off;
log_not_found off;
expires 30d;
}
}
@makecontact
Copy link

Config works fine for me.

Incase you missed it remember to change the paths of the rewrite rules relative to your install e.g.

location /w/ { rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last; }

Might be:

location /sendy/w/ { rewrite ^/sendy/w/([a-zA-Z0-9/]+)$ /sendy/w.php?i=$1 last; }

Note THREE places where you must add the full path.

To be honest, I didn't want to risk this incase there are some direct links in emails and sendy's backend to worry about so I just installed it in the root folder - in which case the above config works fine.

@viet-wego
Copy link

@viet, @dillonbailey I edit some config and it work for me:

location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
 }

Hope that work for you too.

P/S: I install sendy in subdomain.

@ahmadawais
Copy link

It is hacky but works though it is not parsing the URLs. I cannot parse domain.tld/login and instead have to add domain.tld/login.php

@ahmadawais
Copy link

This worked for me, make sure there are no other rules duplication or overlap

 location / {
         try_files $uri $uri/ $uri.php?$args;
     }
     location /l/ {
         rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
     }
     location /t/ {
         rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
     }
     location /w/ {
         rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
     }
     location /unsubscribe/ {
         rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
     }
     location /subscribe/ {
         rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
     }

     # location ~ \.php$ {
     #     try_files $uri =404;
     #     fastcgi_split_path_info ^(.+\.php)(/.+)$;
     #     fastcgi_pass php:9000;
     #     fastcgi_index index.php;
     #     include fastcgi_params;
     #     fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
     #     fastcgi_param PATH_INFO $fastcgi_path_info;
     # }

     location ~ \.php$ {
             try_files $uri =404;
             fastcgi_split_path_info ^(.+\.php)(/.+)$;
             fastcgi_pass 127.0.0.1:9000;
             fastcgi_index index.php;
             fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
             include fastcgi_params;
      }

@martingrega
Copy link

martingrega commented Oct 25, 2017

Hey,
there is a bug in the config. You forgot to escape second dot.
location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
should be
location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|xml)$ {

Our randomly generated tracking link ended with characters "pNg" and all links in newsletter stopped working.

@mj6uc
Copy link

mj6uc commented Nov 7, 2017

Thanks @perceptiveIO, your observation saved the day! :-)

@codegeek1001
Copy link

For me, this one worked for prettifying urls

 location / {
        try_files $uri $uri/ /$uri.php?is_args$args;
}

@Lemmings19
Copy link

Installing Sendy at https://mydomain.com/sendy, this worked for me:

    location /sendy {
        rewrite ^/sendy/(l|t|w)/([a-zA-Z0-9\/]+)$ /sendy/$1.php?i=$2&$args;
        rewrite ^/sendy/(u?n?subscribe)/(.*)$ /sendy/$1.php?i=$2&$args;
        try_files $uri $uri/ $uri.php?$args;

        location ~ \.php$ {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }

If you're having issues, check https://mydomain.com//sendy/_compatibility.php?i=1 to see if anything isn't configured properly. mod_rewrite is not enabled will probably always show up if you're using Nginx.

@ttk
Copy link

ttk commented Aug 2, 2018

There is a bug on line 46 of the example code, the period needs to be escaped...

Should be:

location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
        access_log off;
        log_not_found off;
        expires 30d;
    }

@gnif
Copy link

gnif commented Sep 23, 2018

All these rewrites could be simplified to a single location match and rewrite like so:

location ~ ^\/(l|t|w|(un)?subscribe)\/ {
  rewrite ^\/([^/]+)\/(.+)$ "/$1.php?i=$2" last;
}

@saaiful
Copy link

saaiful commented May 3, 2019

This configuration works for me in NGiNX.
https://gist.github.com/saaiful/6f0f32de64d5fccd793b3ce5059c8bb2

@cartpauj
Copy link

I tried a whole bunch of stuff. This is what finally worked for me (with certbot https). Sub-domain install of Sendy using nginx and SSL/TLS.

# Marketing (Sendy) Installation
#
server {
        root /var/www/marketing.site.com;

        index index.php index.html index.htm;

        server_name marketing.site.com;

        autoindex off;

        add_header X-Robots-Tag "noindex, noarchive";

        location = /favicon.ico { log_not_found off; access_log off; }
        location = /robots.txt { log_not_found off; access_log off; allow all; }
        location ~ /\.  { deny all; log_not_found off; access_log off; return 404; }

        location / {
                try_files $uri $uri/ /$uri.php$is_args$args; # $is_args converts to a ? if true
        }

        location /l/ {
                rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
        }

        location /t/ {
                rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
        }

        location /w/ {
                rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
        }

        location /unsubscribe/ {
                rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
        }

        location /subscribe/ {
                rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
        }

        location /confirm/ {
                rewrite ^/confirm/(.*)$ /confirm.php?i=$1 last;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }

        location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
                access_log off;
                log_not_found off;
                expires 30d;
        }

        # Added by certbot - remove these when copying this vhost to a new server block
        # listen [::]:443 ssl http2; # managed by Certbot
        listen 443 ssl http2; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/marketing.site.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/marketing.site.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
        if ($host = marketing.site.com) {
                return 301 https://$host$request_uri;
        } # managed by Certbot

        listen 80;
        # listen [::]:80;

        server_name marketing.site.com;
        return 404; # managed by Certbot
}

@informdev
Copy link

Do we not need to exclude some things like scheduled.php ?

@sudo-shubham
Copy link

I tried a whole bunch of stuff. This is what finally worked for me (with certbot https). Sub-domain install of Sendy using nginx and SSL/TLS.

# Marketing (Sendy) Installation
#
server {
        root /var/www/marketing.site.com;

        index index.php index.html index.htm;

        server_name marketing.site.com;

        autoindex off;

        add_header X-Robots-Tag "noindex, noarchive";

        location = /favicon.ico { log_not_found off; access_log off; }
        location = /robots.txt { log_not_found off; access_log off; allow all; }
        location ~ /\.  { deny all; log_not_found off; access_log off; return 404; }

        location / {
                try_files $uri $uri/ /$uri.php$is_args$args; # $is_args converts to a ? if true
        }

        location /l/ {
                rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
        }

        location /t/ {
                rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
        }

        location /w/ {
                rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
        }

        location /unsubscribe/ {
                rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
        }

        location /subscribe/ {
                rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
        }

        location /confirm/ {
                rewrite ^/confirm/(.*)$ /confirm.php?i=$1 last;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }

        location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
                access_log off;
                log_not_found off;
                expires 30d;
        }

        # Added by certbot - remove these when copying this vhost to a new server block
        # listen [::]:443 ssl http2; # managed by Certbot
        listen 443 ssl http2; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/marketing.site.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/marketing.site.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
        if ($host = marketing.site.com) {
                return 301 https://$host$request_uri;
        } # managed by Certbot

        listen 80;
        # listen [::]:80;

        server_name marketing.site.com;
        return 404; # managed by Certbot
}

I Love you man.

@joglomedia
Copy link

Hello, I have Nginx 1.4.6 with Sendy 2.0.7. With this configuration i get mod_rewrite is not enabled

mod_rewrite is Apache module, so PHP running with Nginx will not able to detect it

I use LEMPer to deploy Sendy its quiet easy

@tucq88
Copy link

tucq88 commented Nov 8, 2019

https://gist.github.com/refringe/6545132#gistcomment-2925206

I tried a whole bunch of stuff. This is what finally worked for me (with certbot https). Sub-domain install of Sendy using nginx and SSL/TLS.

Worked with me, thanks @cartpauj

@yannickdirbe
Copy link

yannickdirbe commented May 2, 2020

try this
`autoindex off;

add_header X-Robots-Tag "noindex, noarchive";

location / {
    try_files $uri $uri/ /$uri.php?$query_string;
}

location /l/ {
    rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
}
location /t/ {
    rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
}
location /w/ {
    rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
}
location /unsubscribe/ {
    rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
}
location /subscribe/ {
    rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
}

location ~ \.php$ {
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
    access_log off;
    log_not_found off;
    expires 30d;
}

location ~ /\.(?!well-known).* {
    deny all;
}`

@easynotedotcom
Copy link

I tried a whole bunch of stuff. This is what finally worked for me (with certbot https). Sub-domain install of Sendy using nginx and SSL/TLS.

# Marketing (Sendy) Installation
#
server {
        root /var/www/marketing.site.com;

        index index.php index.html index.htm;

        server_name marketing.site.com;

        autoindex off;

        add_header X-Robots-Tag "noindex, noarchive";

        location = /favicon.ico { log_not_found off; access_log off; }
        location = /robots.txt { log_not_found off; access_log off; allow all; }
        location ~ /\.  { deny all; log_not_found off; access_log off; return 404; }

        location / {
                try_files $uri $uri/ /$uri.php$is_args$args; # $is_args converts to a ? if true
        }

        location /l/ {
                rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
        }

        location /t/ {
                rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
        }

        location /w/ {
                rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
        }

        location /unsubscribe/ {
                rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
        }

        location /subscribe/ {
                rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
        }

        location /confirm/ {
                rewrite ^/confirm/(.*)$ /confirm.php?i=$1 last;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }

        location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
                access_log off;
                log_not_found off;
                expires 30d;
        }

        # Added by certbot - remove these when copying this vhost to a new server block
        # listen [::]:443 ssl http2; # managed by Certbot
        listen 443 ssl http2; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/marketing.site.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/marketing.site.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
        if ($host = marketing.site.com) {
                return 301 https://$host$request_uri;
        } # managed by Certbot

        listen 80;
        # listen [::]:80;

        server_name marketing.site.com;
        return 404; # managed by Certbot
}

Just tested this and worked like a charm :) Thank you!

@alexgarciab
Copy link

Thanks @cartpauj. In my case, what I needed to pay attention to was this:

location / {
                try_files $uri $uri/ /$uri.php$is_args$args;
        }

@amanjuman
Copy link

location ~ ^/(l|t|w|(un)?subscribe)/ {
rewrite ^/([^/]+)/(.+)$ "/$1.php?i=$2" last;
}

Correction

location ~ ^\/(l|t|w|confirm|(un)?subscribe)\/
{
	rewrite ^\/([^/]+)\/(.+)$ "/$1.php?i=$2" last;
}

@ulfie22
Copy link

ulfie22 commented Aug 29, 2021

Here is a working config for Sendy v5.2.3 and Forge as of 8-28-2021

(1) change "yourdomain.com" to your domain
(2) note that you should use this to get the site installed, then add SSL in Forge, then make sure your sendy includes/config.php file uses "https://" in the APP_PATH

# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/sendy.yourdomain.com/before/*;

server {
    listen 80;
    listen [::]:80;
    server_name sendy.yourdomain.com;
    root /home/forge/sendy.yourdomain.com/;

    # FORGE SSL (DO NOT REMOVE!)
    # ssl_certificate;
    # ssl_certificate_key;
    
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    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:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!3DES';
    ssl_prefer_server_ciphers on;
    ssl_dhparam /etc/nginx/dhparams.pem;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    autoindex off;

    index index.html index.htm index.php;

    charset utf-8;

    # FORGE CONFIG (DO NOT REMOVE!)
    include forge-conf/sendy.yourdomain.com/server/*;

        location / {
        try_files $uri $uri/ $uri.php?$args;
    }

    location /l/ {
        rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
    }

    location /t/ {
        rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
    }

    location /w/ {
        rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
    }

    location /unsubscribe/ {
        rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
    }

    location /subscribe/ {
        rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    access_log off;
    error_log  /var/log/nginx/sendy.yourdomain.com-error.log error;

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/sendy.yourdomain.com/after/*;


@adarmanto
Copy link

adarmanto commented Nov 9, 2021

This NGINX config for set up the Sendy into subdirectory:

https://example.com -> /var/www/example.com/public
https://example.com/sendy -> /var/www/sendy

server {
    root /var/www/example.com/public;
    index index.php index.html;
    server_name example.com www.example.com;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    }

    location ~ /\.ht {
        deny all;
    }

    # Sendy
    location /sendy {
        alias /var/www/sendy;
        try_files $uri $uri/ @sendy;

        location ~ \.php$ {
           include snippets/fastcgi-php.conf;
           fastcgi_param SCRIPT_FILENAME $request_filename;
           fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        }
    }

    location @sendy {
        rewrite /sendy/(l|t|w)/([a-zA-Z0-9\/]+)$ /sendy/$1.php?i=$2 last;
        rewrite /sendy/(u?n?subscribe)/(.*)$ /sendy/$1.php?i=$2 last;
        rewrite /sendy/(.*)$ /sendy/$1.php last;
    }
    # End Sendy
}

Reference: https://serversforhackers.com/c/nginx-php-in-subdirectory

@anil1kuppa
Copy link

Thanks a lot @cartpauj https://gist.github.com/refringe/6545132#gistcomment-2925206 for this. It has helped me to fix.

@ahmadawais
Copy link

Bascially all you really need is this

location / {
		try_files $uri $uri/ $uri.html $uri.php$is_args$query_string;
	}

But to be on the safe side I added everything below:

location / {
		try_files $uri $uri/ $uri.html $uri.php$is_args$query_string;
	}

	location /l/ {
                rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
        }

        location /t/ {
                rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
        }

        location /w/ {
                rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
        }

        location /unsubscribe/ {
                rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
        }

        location /subscribe/ {
                rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
        }

        location /confirm/ {
                rewrite ^/confirm/(.*)$ /confirm.php?i=$1 last;
        }

And it works really well.

@zuhairthabit01
Copy link

this will ensure that you are using a pretty URL

location / {
		try_files $uri $uri/ $uri.html $uri.php$is_args$query_string;
	}

and this will ensure that you are using a pretty URL with sendy settings.

location / {
		try_files $uri $uri/ $uri.html $uri.php$is_args$query_string;
	}

	location /l/ {
                rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
        }

        location /t/ {
                rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
        }

        location /w/ {
                rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
        }

        location /unsubscribe/ {
                rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
        }

        location /subscribe/ {
                rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
        }

        location /confirm/ {
                rewrite ^/confirm/(.*)$ /confirm.php?i=$1 last;
        }

@De-Geek
Copy link

De-Geek commented Sep 29, 2023

I tried a whole bunch of stuff. This is what finally worked for me (with certbot https). Sub-domain install of Sendy using nginx and SSL/TLS.

# Marketing (Sendy) Installation
#
server {
        root /var/www/marketing.site.com;

        index index.php index.html index.htm;

        server_name marketing.site.com;

        autoindex off;

        add_header X-Robots-Tag "noindex, noarchive";

        location = /favicon.ico { log_not_found off; access_log off; }
        location = /robots.txt { log_not_found off; access_log off; allow all; }
        location ~ /\.  { deny all; log_not_found off; access_log off; return 404; }

        location / {
                try_files $uri $uri/ /$uri.php$is_args$args; # $is_args converts to a ? if true
        }

        location /l/ {
                rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
        }

        location /t/ {
                rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
        }

        location /w/ {
                rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
        }

        location /unsubscribe/ {
                rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
        }

        location /subscribe/ {
                rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
        }

        location /confirm/ {
                rewrite ^/confirm/(.*)$ /confirm.php?i=$1 last;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }

        location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
                access_log off;
                log_not_found off;
                expires 30d;
        }

        # Added by certbot - remove these when copying this vhost to a new server block
        # listen [::]:443 ssl http2; # managed by Certbot
        listen 443 ssl http2; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/marketing.site.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/marketing.site.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
        if ($host = marketing.site.com) {
                return 301 https://$host$request_uri;
        } # managed by Certbot

        listen 80;
        # listen [::]:80;

        server_name marketing.site.com;
        return 404; # managed by Certbot
}

Thanks for this, it also worked for me but I found that I could not login. Using chatGPT I added the below after the other "add_header " and I could now login securely.

add_header Content-Security-Policy "upgrade-insecure-requests";

@luisrock
Copy link

I tried a whole bunch of stuff. This is what finally worked for me (with certbot https). Sub-domain install of Sendy using nginx and SSL/TLS.

# Marketing (Sendy) Installation
#
server {
        root /var/www/marketing.site.com;

        index index.php index.html index.htm;

        server_name marketing.site.com;

        autoindex off;

        add_header X-Robots-Tag "noindex, noarchive";

        location = /favicon.ico { log_not_found off; access_log off; }
        location = /robots.txt { log_not_found off; access_log off; allow all; }
        location ~ /\.  { deny all; log_not_found off; access_log off; return 404; }

        location / {
                try_files $uri $uri/ /$uri.php$is_args$args; # $is_args converts to a ? if true
        }

        location /l/ {
                rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
        }

        location /t/ {
                rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
        }

        location /w/ {
                rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
        }

        location /unsubscribe/ {
                rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
        }

        location /subscribe/ {
                rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
        }

        location /confirm/ {
                rewrite ^/confirm/(.*)$ /confirm.php?i=$1 last;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }

        location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
                access_log off;
                log_not_found off;
                expires 30d;
        }

        # Added by certbot - remove these when copying this vhost to a new server block
        # listen [::]:443 ssl http2; # managed by Certbot
        listen 443 ssl http2; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/marketing.site.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/marketing.site.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
        if ($host = marketing.site.com) {
                return 301 https://$host$request_uri;
        } # managed by Certbot

        listen 80;
        # listen [::]:80;

        server_name marketing.site.com;
        return 404; # managed by Certbot
}

That helped. Thanks!

@elfico
Copy link

elfico commented Feb 5, 2024

Thanks to everyone who contributed to this.
For anyone that is hosting in Azure App Service for Linux which uses nginx,
What I would advise is to first copy the original nginx file and then edit it based on the config above.

First:
cp /etc/nginx/sites-available/default /home/site/default
The above will copy the default file from the etc/nginx/sites-available to your deployment folder.
For example, this is what I got:

server {
    #proxy_cache cache;
	#proxy_cache_valid 200 1s;
    listen 8080;
    listen [::]:8080;
    root /home/site/wwwroot;
    index  index.php index.html index.htm;
    server_name  example.com www.example.com; 
    port_in_redirect off;

    location / {            
        index  index.php index.html index.htm hostingstart.html;
    }

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /html/;
    }
    
    # Disable .git directory
    location ~ /\.git {
        deny all;
        access_log off;
        log_not_found off;
    }

    # Add locations of phpmyadmin here.
    location ~* [^/]\.php(/|$) {
        fastcgi_split_path_info ^(.+?\.[Pp][Hh][Pp])(|/.*)$;
        fastcgi_pass 127.0.0.1:9000;
        include fastcgi_params;
        fastcgi_param HTTP_PROXY "";
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_intercept_errors on;
        fastcgi_connect_timeout         300; 
        fastcgi_send_timeout           3600; 
        fastcgi_read_timeout           3600;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 4 256k;
        fastcgi_busy_buffers_size 256k;
        fastcgi_temp_file_write_size 256k;
    }
}

Then we can edit it based on the config shared above.
*Note, the main location / ... should include what you had from the original file. I went through pain because of this.

Also, the config for location ~* [^/]\.php(/|$) {.... should be kept
Final config is:

server {
    #proxy_cache cache;
	#proxy_cache_valid 200 1s;
    listen 8080;
    listen [::]:8080;
    root /home/site/wwwroot;
    index  index.php index.html index.htm;
    server_name www.example.com;
    autoindex off;

    port_in_redirect off;

    location / {            
        index  index.php index.html index.htm hostingstart.html;  #make sure to include this
        try_files $uri $uri/ $uri.html $uri.php$is_args$query_string;
    }

    location /l/ {
        rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
    }

    location /t/ {
        rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
    }

    location /w/ {
        rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
    }

    location /unsubscribe/ {
        rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
    }

    location /subscribe/ {
        rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
    }

    location /confirm/ {
        rewrite ^/confirm/(.*)$ /confirm.php?i=$1 last;
    }

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /html/;
    }
    
    # Disable .git directory
    location ~ /\.git {
        deny all;
        access_log off;
        log_not_found off;
    }

    # Did not replace this
    location ~* [^/]\.php(/|$) {
        fastcgi_split_path_info ^(.+?\.[Pp][Hh][Pp])(|/.*)$;
        fastcgi_pass 127.0.0.1:9000;
        include fastcgi_params;
        fastcgi_param HTTP_PROXY "";
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_index index.php;
        fastcgi_intercept_errors on;
        fastcgi_connect_timeout         300; 
        fastcgi_send_timeout           3600; 
        fastcgi_read_timeout           3600;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 4 256k;
        fastcgi_busy_buffers_size 256k;
        fastcgi_temp_file_write_size 256k;
    }

    location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
        access_log off;
        log_not_found off;
        expires 30d;
    }
}

Then run cp /home/site/default /etc/nginx/sites-available/default && service nginx restart via SSH

And also add cp /home/site/default /etc/nginx/sites-available/default && service nginx restart in the Startup Command under Configuration in your Azure Portal

@gnif
Copy link

gnif commented Feb 5, 2024

You do realise this is extremely bad practice as you are exposing your http configuration to the world by placing it in your www root directory?

@elfico
Copy link

elfico commented Feb 5, 2024

You do realise this is extremely bad practice as you are exposing your http configuration to the world by placing it in your www root directory?

Thanks for catching this!. Not sure how the files are served in nginx.

Moved it to a different folder.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment