This tutorial sets up a single WordPress site on a server. If you would like to have multiple sites (and domain names) on the same server, go here.
First, setup your Ubuntu server.
Super-fast Secure WordPress Install on DigitalOcean with NGINX, PHP7, and Ubuntu 16.04 LTS
http://www.morphatic.com/2016/05/21/super-fast-secure-wordpress-install-on-digitalocean-with-nginx-php7-and-ubuntu-16-04-lts/
1. Configure Your Domain Name
First, create your droplet, see here:
https://www.digitalocean.com/community/tutorials/how-to-create-your-first-digitalocean-droplet-virtual-server
Next, login to the site where you registered your domain name and change the nameservers to point to:
- ns1.digitalocean.com
- ns2.digitalocean.com
- ns3.digitalocean.com
Then, go to your DigitalOcean account and go to Networking > Domains. In the form to “Add a Domain” enter your domain name and then select the droplet you created.
We will setup our domain to route email through Mailgun’s servers (free accounts available with no credit card needed). To configure your domain in DigitalOcean, see here:
https://www.digitalocean.com/community/tutorials/how-to-set-up-a-host-name-with-digitalocean
When all done, your settings should look something like this (using Mailgun’s servers):
http://code.krister.ee/mailgun-digitalocean/
2. Install NGINX
In order to use a WordPress plugin for purging the NGINX cache that I talk about below, you have to install a custom version of NGINX:
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3050AC3CD2AE6F03
$ sudo sh -c "echo 'deb http://download.opensuse.org/repositories/home:/rtCamp:/EasyEngine/xUbuntu_16.04/ /' >> /etc/apt/sources.list.d/nginx.list"
sudo apt-get update
$ sudo apt-get install nginx-custom
$ sudo ufw allow 'Nginx Full'
$ sudo ufw enable
update settings in ‘nginx.conf' by commenting out all lines in the ‘SSL Settings' section by putting ‘#' in from of them (these will conflict with some of the settings we create later):
sudo nano /etc/nginx/nginx.conf
after you have done this, it should look like this:
##
# SSL Settings
##
#ssl_session_cache shared:SSL:20m;
#ssl_session_timeout 10m;
#ssl_prefer_server_ciphers on;
#ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:$
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
CTRL-X to save, then ‘Y' and ENTER to exit.
3. Install MariaDB
see MySQL vs MariaDB
$ sudo apt-get install software-properties-common
$ sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8
$ sudo add-apt-repository 'deb [arch=amd64,i386] http://sfo1.mirrors.digitalocean.com/mariadb/repo/10.1/ubuntu xenial main'
$ sudo apt-get update
$ sudo apt-get install mariadb-server
Set a password for MariaDB (recommended to use a different password than your user/root account).
Next:
$ sudo mysql_secure_installation
Enter ‘no' when asked to change password (since we just setup a new one) and ‘yes' to the rest of the prompts.
Login to MariaDB
$ mysql -u root -p
Create a new database, user and assign privileges to the user:
MariaDB [(none)]> CREATE DATABASE mywpdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
MariaDB [(none)]> GRANT ALL ON mywpdb.* TO 'mywpdbuser'@'localhost' IDENTIFIED BY 'securepassword';
MariaDB [(none)]> FLUSH PRIVILEGES;
MariaDB [(none)]> quit
enter ‘exit' to exit MariaDB
4. Install PHP7-FPM
$ sudo apt-get install -y php-fpm php-mysql php-xml php-gd php7.0-zip
Adjust php settings:
$ sudo nano /etc/php/7.0/fpm/php.ini
enter CTRL +W to access search function and type ‘cgi.fix_pathinfo'. Where you see the line ‘cgi.fix_pathinfo=1', uncomment the line (by deleting the semicolon) and edit as follows:
cgi.fix_pathinfo=0
Enter CTRL +W again and search for ‘post_max_size' and change this value to whatever size you would like. Do the same for ‘upload_max_filesize':
post_max_size = 100M
upload_max_filesize = 200M
CTRL-X to save, then ‘Y' and ENTER to exit.
Restart php:
$ sudo service php7.0-fpm restart
Open up the configuration file for your default site for NGINX:
$ sudo nano /etc/nginx/sites-available/default
Delete the code inside and copy/paste the code below into the file:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php index.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
CTRL-X to save, then ‘Y' and ENTER to exit.
restart NGINX:
$ sudo service nginx restart
test changes by creating a php file at the root of the web server:
$ echo "<?php phpinfo();" | sudo tee /var/www/html/index.php > /dev/null
Next, go to your web browser and type your server url, http://your-IP-address and you should see the auto-generated PHP info page.
5. Setup SSL Certificates with LetsEncrypt
$ sudo apt-get install letsencrypt
Add some extra code to the NGINX configuration file so that the entire file looks like this:
$ sudo nano /etc/nginx/sites-available/default
then,
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php index.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location ~ /.well-known {
allow all;
}
location ~ /\.ht {
deny all;
}
}
note that we just added the lines (just before the end):
location ~ /.well-known {
allow all;
}
CTRL-X to save, then ‘Y' and ENTER to exit.
check to make sure the NGINX syntax is correct:
$ sudo nginx -t
you should see something like:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
restart NGINX:
$ sudo service nginx restart
Next,
$ sudo letsencrypt certonly -a webroot --webroot-path=/var/www/html -d yourdomain.com -d www.yourdomain.com
Enter email address and accept license.
If everything works OK, you should see something like this:
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/yourdomain.com/fullchain.pem. Your cert
will expire on 2017-01-22. To obtain a new version of the
certificate in the future, simply run Let's Encrypt again.
- If you like Let's Encrypt, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
To increase security, DigitalOcean’s tutorial recommends setting up a strong Diffie-Hellman Group as follows:
$ sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
Next, we create a configuration file that contains all of our SSL parameters:
$ sudo nano /etc/nginx/snippets/ssl-params.conf
Then, add this code into the file, making sure you add your domain at the top in the file paths for the ssl_certificate and ssl_certificate_key:
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# code below from https://cipherli.st/
# and https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
CTRL-X to save, then ‘Y' and ENTER to exit.
Next we will update our NGINX configuration for our site again to redirect all traffic through HTTPS.
$ sudo nano /etc/nginx/sites-available/default
edit the file to look like this, taking note to edit ‘yourdomain.com' to the domain your using:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
include snippets/ssl-params.conf;
root /var/www/html;
index index.php index.html;
server_name yourdomain.com www.yourdomain.com;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location ~ /.well-known {
allow all;
}
location ~ /\.ht {
deny all;
}
}
CTRL-X to save, then ‘Y' and ENTER to exit.
check to make sure the NGINX syntax is correct:
$ sudo nginx -t
then, restart NGINX:
$ sudo service nginx restart
SSL certificates from LetsEncrypt expire every 90 days. So you don’t have to log into your server every 3 months to renew your certs, we set up a CRON job to autorenew them:
$ sudo crontab -e
You will be prompted to select an editor, select ‘2' (nano):
Then add the following lines at the bottom after the commented section:
30 2 * * 1 /usr/bin/letsencrypt renew >> /var/log/le-renew.log
35 2 * * 1 /bin/systemctl reload nginx
CTRL-X to save, then ‘Y' and ENTER to exit.
This will update the LetsEncrypt client and then attempt to renew and load your certs (if necessary) every Monday.
6. Install WordPress
First, we will install Subversion:
$ sudo apt-get install -y subversion
Then, go to web root directory, remove index.php (if necessary), and note the most recent version of WordPress:
$ cd /var/www/html
$ sudo rm index.php
$ sudo svn co http://core.svn.wordpress.org/tags/4.6.1 .
note the ‘.' (period) at the end of the last command.
Then, update the ownership of the files:
$ sudo chown -R www-data:www-data .
again, note the ‘.' (period) at the end of the command.
Now you can visit your domain in a web browser and complete the installation by following the WordPress prompts. Note the database name and user that you created in step 2.
7. Installing WordPress plugins and setting up email.
to allow updates of WordPress without FTP, add this line to the ‘wp-config' file right before the line ‘That's all, stop editing!':
/** auto update without FTP */
define('FS_METHOD','direct');
alternatively, if you would like to allow file downloads through FTPS, see this post:
http://www.morphatic.com/2016/05/21/super-fast-secure-wordpress-install-on-digitalocean-with-nginx-php7-and-ubuntu-16-04-lts/
First install the nginx helper plugin. After loggin in to the WP admin panel, go to Plugins > Add New, and in the ‘Search Plugins' box enter ‘nginx plugin helper'. Click ‘Install Now' and once completed, click ‘Activate'.Then, go to Settings > Nginx Helper from the WP dashboard and check the box to “Enable Purge.” The default settings should be fine. Click ‘Save All Changes'.
From Step 1 above, we will use the Mailgun for WordPress plugin.
Go to Plugins > Add New, and in the ‘Search Plugins' box enter ‘mailgun'. Click ‘Install Now' and once completed, click ‘Activate'.
Then, go to Settings > Mailgun in the WP dashboard and copy/paste in your Mailgun domain name and API key, then click ‘Save Changes'. Click ‘Test Configuration' to make sure it is working. You can also use the Check Email plugin just to make sure that emails are being sent correctly.
Inside of your Mailgun dashboard, you may have to go to Domains > yourDomainName, go down the page ‘Security Settings For Outgoing Mail', click ‘Certificate Verification' and select ‘Not Required' (‘TLS Connection' should already be set to ‘Opportunistic').
8. Optimizing WordPress
Setup caching and purging. Edit NGINX configuration file to below, note to make sure ‘yoursite.com' and ‘www.yoursite.com' is changed to your domain name:
$ sudo nano /etc/nginx/sites-available/default
then copy/paste this in,
fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name yoursite.com www.yoursite.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
include snippets/ssl-params.conf;
client_max_body_size 256M;
root /var/www/html;
index index.php index.html;
server_name yoursite.com www.yoursite.com;
set $skip_cache 0;
if ($request_method = POST) {
set $skip_cache 1;
}
if ($query_string != "") {
set $skip_cache 1;
}
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
set $skip_cache 1;
}
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
set $skip_cache 1;
}
autoindex off;
location ~ /purge(/.*) {
fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
}
location ~* ^.+\.(flv|pdf|avi|mov|mp3|wmv|m4v|webm|aac|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
expires max;
log_not_found off;
access_log off;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 60m;
include fastcgi_params;
}
location ~* ^/wp-includes/.*(?<!(js/tinymce/wp-tinymce))\.php$ {
internal;
}
location = /robots.txt {
access_log off;
log_not_found off;
}
location = /wp-config.php {
deny all;
}
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
location ~* ^/wp-content/.*\.(txt|md|exe|sh|bak|inc|php|pot|po|mo|log|sql)$ {
deny all;
}
location ~ /\.(ht|svn)? {
deny all;
}
location ~ /.well-known {
allow all;
}
}
Next, to make sure unattended upgrades are enabled:
$ sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
uncomment these lines (delete the forward slashes in beginning of the line) and change values where necessary:
"${distro_id}:${distro_codename}-updates";
Unattended-Upgrade::Mail "[email protected]";
Unattended-Upgrade::Remove-Unused-Dependencies "false";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "02:00";
Complete file looks like this:
// Automatically upgrade packages from these (origin:archive) pairs
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
"${distro_id}:${distro_codename}-updates";
// "${distro_id}:${distro_codename}-proposed";
// "${distro_id}:${distro_codename}-backports";
};
// List of packages to not update (regexp are supported)
Unattended-Upgrade::Package-Blacklist {
// "vim";
// "libc6";
// "libc6-dev";
// "libc6-i686";
};
// This option allows you to control if on a unclean dpkg exit
// unattended-upgrades will automatically run
// dpkg --force-confold --configure -a
// The default is true, to ensure updates keep getting installed
//Unattended-Upgrade::AutoFixInterruptedDpkg "false";
// Split the upgrade into the smallest possible chunks so that
// they can be interrupted with SIGUSR1. This makes the upgrade
// a bit slower but it has the benefit that shutdown while a upgrade
// is running is possible (with a small delay)
//Unattended-Upgrade::MinimalSteps "true";
// Install all unattended-upgrades when the machine is shuting down
// instead of doing it in the background while the machine is running
// This will (obviously) make shutdown slower
//Unattended-Upgrade::InstallOnShutdown "true";
// Send email to this address for problems or packages upgrades
// If empty or unset then no email is sent, make sure that you
// have a working mail setup on your system. A package that provides
// 'mailx' must be installed. E.g. "[email protected]"
Unattended-Upgrade::Mail "[email protected]";
// Set this value to "true" to get emails only on errors. Default
// is to always send a mail if Unattended-Upgrade::Mail is set
//Unattended-Upgrade::MailOnlyOnError "true";
// Do automatic removal of new unused dependencies after the upgrade
// (equivalent to apt-get autoremove)
Unattended-Upgrade::Remove-Unused-Dependencies "true";
// Automatically reboot *WITHOUT CONFIRMATION*
// if the file /var/run/reboot-required is found after the upgrade
Unattended-Upgrade::Automatic-Reboot "true";
// If automatic reboot is enabled and needed, reboot at the specific
// time instead of immediately
// Default: "now"
Unattended-Upgrade::Automatic-Reboot-Time "02:00";
// Use apt bandwidth limit feature, this example limits the download
// speed to 70kb/sec
//Acquire::http::Dl-Limit "70";
Then,
sudo nano /etc/apt/apt.conf.d/10periodic
edit the file to look like this:
PT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "0";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
Finally,
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get autoclean
$ sudo reboot
9. Installing phpMyAdmin
Some code taken from here, but edited for PHP7-FPM / Ubuntu 16.04:
https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-phpmyadmin-with-nginx-on-an-ubuntu-14-04-server
$ sudo apt-get install phpmyadmin
During the installation, you will be prompted for some information. It will ask you which web server you would like the software to automatically configure. Since Nginx, the web server we are using, is not one of the available options, you can just hit TAB to bypass this prompt.
The next prompt will ask if you would like dbconfig-common to configure a database for phpmyadmin to use. Select “Yes” to continue.
You will need to enter the database administrative password that you configured during the MariaDB installation to allow these changes. Afterward, you will be asked to select and confirm a password for a new database that will hold ~phpMyAdmin's own data.
We need to address is enabling the mcrypt PHP module, which phpMyAdmin relies on. This was installed with phpMyAdmin so we just need to toggle it on and restart our PHP processor:
sudo apt-get install mcrypt php7.0-mcrypt
sudo service php7.0-fpm restart
The installation will now complete. For the Nginx web server to find and serve the ~phpMyAdmin files correctly, we just need to create a symbolic link from the installation files to our Nginx document root directory by typing this:
sudo ln -s /usr/share/phpmyadmin /usr/share/nginx/html
the above was the recommended method but did not work – when I went to mydomain.com/phpmyadmin only mydomain.com displayed. The solution was found somewhere in the comments, below the section referenced here:
https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-phpmyadmin-with-nginx-on-an-ubuntu-14-04-server?comment=36631
the solution was:
sudo ln -s /usr/share/phpmyadmin /var/www/html
Now let's make this more secure. We will change the location of the interface from /phpmyadmin to something else to sidestep some of the automated bot brute-force attempts. We will also create an additional, web server-level authentication gateway that must be passed before even getting to the phpMyAdmin login screen. Change ‘nothingtosee' to anything you want.
cd /var/www/html
sudo mv phpmyadmin nothingtosee
Now to go to phpmyadmin, you must go to https://mydomain.com/nothingtosee
Next we add an authentication prompt that a user would be required to pass before ever seeing the phpMyAdmin login screen.
We first will create a password file that will store our the authentication credentials. Nginx requires that passwords be encrypted using the crypt() function. The OpenSSL suite, which should already be installed on your server, includes this functionality.
To create an encrypted password, type:
openssl passwd
above command produces errors.
You can add a username to the file using this command, you can use whatever name you like:
$ sudo sh -c "echo -n 'username:' >> /etc/nginx/.htpasswd"
Next, add an encrypted password entry for the username by typing:
sudo sh -c "openssl passwd -apr1 >> /etc/nginx/.htpasswd"
You can repeat this process for additional usernames. You can see how the usernames and encrypted passwords are stored within the file by typing:
$ cat /etc/nginx/.htpasswd
output is something like:
username:$apr1$wI1/T0nB$jEK... ...
Next, we need to configure Nginx to check this file before serving our protected content.
$ sudo nano /etc/nginx/sites-enabled/default
You can set restrictions on the server level or inside a specific location. We use the auth_basic directive to turn on authentication and to choose a realm name to be displayed to the user when prompting for credentials. We will use the auth_basic_user_file directive to point Nginx to the password file we created and restrict our phpmyadmin folder/link that we created above ‘nothingtosee':
https://www.scalescale.com/tips/nginx/protect-directory-user-password-nginx/
location ^~ /nothingtosee/ {
auth_basic "Restricted Content";
auth_basic_user_file /etc/nginx/.htpasswd;
location ~ .php$ {
root /var/www/yourdomain.com;}
}
Now the directory is password protected. the entire file looks like this:
fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
include snippets/ssl-params.conf;
client_max_body_size 256M;
root /var/www/html;
index index.php index.html;
server_name yourdomain.com www.yourdomain.com;
set $skip_cache 0;
if ($request_method = POST) {
set $skip_cache 1;
}
if ($query_string != "") {
set $skip_cache 1;
}
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
set $skip_cache 1;
}
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
set $skip_cache 1;
}
autoindex off;
location ~ /purge(/.*) {
fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
}
location ~* ^.+\.(flv|pdf|avi|mov|mp3|wmv|m4v|webm|aac|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
expires max;
log_not_found off;
access_log off;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 60m;
include fastcgi_params;
}
location ~* ^/wp-includes/.*(?<!(js/tinymce/wp-tinymce))\.php$ {
internal;
}
location = /robots.txt {
access_log off;
log_not_found off;
}
location = /wp-config.php {
deny all;
}
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
location ~* ^/wp-content/.*\.(txt|md|exe|sh|bak|inc|php|pot|po|mo|log|sql)$ {
deny all;
}
location ~ /\.(ht|svn)? {
deny all;
}
location ~ /.well-known {
allow all;
}
location ^~ /nothingtosee/ {
auth_basic "Restricted Content";
auth_basic_user_file /etc/nginx/.htpasswd;
location ~ .php$ {
root /var/www/yourdomain.com;}
}
}