Migrate from Bazel

This commit is contained in:
Marc Plano-Lesay 2025-12-12 15:30:04 +11:00
commit 016dbd0814
Signed by: kernald
GPG key ID: 66A41B08CC62A6CF
59 changed files with 7044 additions and 0 deletions

View file

@ -0,0 +1,464 @@
+++
template = "article.html"
title = "Hosting different kinds of apps on nginx"
date = 2014-10-15T10:55:00+02:00
description = "An introduction to nginx as a web server and reverse proxy, covering how to host static sites, PHP applications, and Node.js apps."
[taxonomies]
tags = ["nginx", "web"]
+++
## Engine what?
Nginx (engine-x) is a web server and reverse proxy for web and mail protocols
(HTTP, HTTPS, SMTP, POP3 and IMAP). It has been first released in 2004, and its
usage keeps growing ever since (according to
[Netcraft](http://news.netcraft.com/archives/2014/08/27/august-2014-web-server-survey.html),
it was hosting 14.47% of active sites in August 2014).
It's capable of hosting many kinds of applications:
- static HTML pages
- PHP, using [PHP-FPM](http://en.wikipedia.org/wiki/PHP#PHPFPM)
- Ruby on Rails and any kind of Rack-based Ruby application, using
[Phusion Passenger](https://www.phusionpassenger.com/)
- proxying requests to another webserver (e.g. a software launching its own web
server, like [Kodi](http://xbmc.org/))
<!--more-->
## Set up the bases
The architecture described in this post is pretty simple:
- a default virtual host (vhost) for the top-level domain name, also catching
requests to unknown sub-domains
- different applications hosted on sub-domains
- some vhosts will be HTTPS-only, some will offer it without being mandatory
- enabling or disabling a vhost must be easy
### Installing nginx
Nginx uses static modules, enabled or disabled at compile-time. It's important
to decide what you need before installing nginx. The only non-default module
used in this post is Passenger, needed to host Rack-based applications.
Everything else will work without it.
Nginx works on any decent \*nix. It's probably available in your OS
repositories. If it's not, please refer to the
[official installation guide](http://wiki.nginx.org/Install). On Archlinux,
a package is available on
[AUR](https://aur.archlinux.org/packages/nginx-passenger) including the
Passenger module:
`yaourt -S nginx-passenger`
### Configuration
Once nginx is installed, we need to configure a basic configuration. I'll refer
to the configuration root directory as `$CONFDIR`. It's usually `/etc/nginx/`.
Note that nginx needs to be restarted to reflect any configuration change.
#### Directory structure
To ease the configuration, we'll split it across three folders:
- `$CONFDIR` will contain all the general files (PHP configuration, main nginx
configuration file…)
- `$CONFDIR/ssl` will contain the SSL certificates
- `$CONFDIR/vhosts` will contain our vhosts definitions
#### Main configuration file
Here's the basic configuration file we'll start with:
{{ filename(body="$CONFDIR/nginx.conf") }}
```nginx
worker_processes auto;
events {
worker_connections 1024;
}
http {
proxy_send_timeout 600s;
proxy_read_timeout 600s;
fastcgi_send_timeout 600s;
fastcgi_read_timeout 600s;
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 0;
gzip on;
index index.html index.htm;
client_max_body_size 2048m;
server {
listen 0.0.0.0;
server_name enoent.fr;
access_log /var/log/nginx/localhost.access_log;
error_log /var/log/nginx/localhost.error_log info;
root /srv/http/localhost;
}
}
```
This file sets up an nginx instance with some decent settings (enable gzip, use
`index.html` or `index.htm` as default index pages…), and defines our default
vhost. It answers to every request targeting the hostname _enoent.fr_. It will
serve static pages found in `/srv/http/localhost`.
## SSL support
As mentioned earlier, we'll have two SSL behaviours depending on the vhost:
- SSL is offered, but not mandatory (vhost answers to both HTTP and HTTPS)
- SSL is offered, and mandatory (vhost answers on HTTPS, and redirect to HTTPS
when it receives a request on HTTP)
We will need two files to define these two behaviours. One of them will have
to be included in every vhost, depending on the SSL politic we want for this
specific vhost.
### Shared configuration
Here we go for the first configuration file:
{{ filename(body="$CONFDIR/ssl_opt.conf") }}
```nginx
ssl_certificate_key /etc/nginx/ssl/ssl-decrypted.key;
add_header Strict-Transport-Security max-age=31536000;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:RC4-SHA:AES256-GCM-SHA384:AES256-SHA256:CAMELLIA256-SHA:ECDHE-RSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:CAMELLIA128-SHA;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
keepalive_timeout 70;
```
You can obviously adapt this file to your specific needs. It defines:
- the SSL key used (`/etc/nginx/ssl/ssl-decrypted.key`)
- a default max-age header
- a list of accepted SSL ciphers
- session, cache and keepalive durations
The other file will define the exact same settings, adding just one directive:
the SSL is mandatory. Instead of copy and paste all of this, here's what we
can do:
{{ filename(body="$CONFDIR/ssl.conf") }}
```nginx
include ssl_opt.conf;
ssl on;
```
### Enabling SSL for a vhost
To enable SSL on a vhost, we'll need to make three or four modifications to the
vhost definition, depending on the SSL policy.
#### Non-mandatory SSL
If the SSL is not mandatory, we'll need to:
- enable listening on port 443 in addition to the default 80
- choose the certificate we want to use
- include the SSL policy file
Here's how it translates, for our first vhost defined earlier:
{{ filename(body="$CONFDIR/nginx.conf (server block only)") }}
```nginx
server {
listen 0.0.0.0:80;
listen 0.0.0.0:443 ssl;
server_name enoent.fr;
access_log /var/log/nginx/localhost.access_log;
error_log /var/log/nginx/localhost.error_log info;
root /srv/http/localhost;
ssl_certificate /etc/nginx/ssl/enoent.fr.crt;
include ssl_opt.conf;
}
```
#### Mandatory SSL
If the SSL is mandatory, we'll need to:
- enable listening on port 443 __instead of__ the default 80
- choose the certificate we want to use
- include the SSL policy file
- redirect HTTP requests to HTTPS
And here's the result for our first vhost:
{{ filename(body="$CONFDIR/nginx.conf (server block only)") }}
```nginx
server {
listen 0.0.0.0:80;
server_name enoent.fr;
rewrite ^ https://$server_name$request_uri? permanent;
}
server {
listen 0.0.0.0:443 ssl;
server_name enoent.fr;
access_log /var/log/nginx/localhost.access_log;
error_log /var/log/nginx/localhost.error_log info;
root /srv/http/localhost;
ssl_certificate /etc/nginx/ssl/enoent.fr.crt;
include ssl.conf;
}
```
The first `server` block is here to do the redirection, as our inital server
only listens on port 443.
## Virtual hosts
As we saw in the [SSL](#ssl-support) part, we can define as many `server` blocks
as we want. Each of them is able to respond to requests targeting different
hostnames or ports. We also saw earlier the `include` directive, allowing us to
include a file in another.
With this in mind, it's pretty simple to set up a vhost pool from which we can
enable or disable some of them easily. Simply put a file per vhost in a
directory, and include it to enable the corresponding vhost, or remove the
include to disable it.
Here are some templates for different virtual hosts, each one containing only
the minimum (no SSL-specific settings, for example).
### Static HTML
We already saw earlier how to define a virtual host when we set up our main
`nginx.conf` file:
{{ filename(body="$CONFDIR/vhosts/static_html.conf") }}
```nginx
server {
listen 0.0.0.0;
server_name enoent.fr;
access_log /var/log/nginx/localhost.access_log;
error_log /var/log/nginx/localhost.error_log info;
root /srv/http/localhost;
}
```
The only interesting directive here is the `root` one. It will map the root of
the web server to this local folder. A request for
`http://enoent.fr/my_awesome_page.html` will return the content of
`/srv/http/localhost/my_awesome_page.html`.
### Reverse proxy
A reverse proxy may be useful when you have a web server already running, and
want to expose it somewhere else. Let's say we have a NAS on our local network,
its web ui being accessible on `http://nas.local:8080`, and we want to expose it
on `http://nas.enoent.fr`, on the default HTTP port:
{{ filename(body="$CONFDIR/vhosts/reverse_proxy.conf") }}
```nginx
server {
listen 0.0.0.0;
server_name nas.enoent.fr;
access_log /var/log/nginx/nas.access_log;
error_log /var/log/nginx/nas.error_log info;
location / {
proxy_headers_hash_max_size 1024;
proxy_headers_hash_bucket_size 128;
proxy_pass http://nas.local:8080;
}
}
```
The `location /` block here defines a behaviour for all requests matching
`nas.enonet.fr/*`. In our case, that's all of them, as we only have one
`location` block.
Inside of it, we have some settings for our reverse proxy (maximum headers
size), and the really interesting part: the `proxy_pass` entry, which defines
where are redirected the incoming requests.
### PHP
To allow PHP applications to work, we'll need a PHP interpreter. More
specifically, we'll use [PHP-FPM](http://php-fpm.org/). PHP-FPM is a FastCGI PHP
processor. It's a daemon listening on a socket, waiting for PHP scripts, and
returning the PHP output. The configuration of PHP-FPM is out of this article
scope, but we'll need to have it running, and note where it can be acceded (a
local Unix socket, or a TCP socket, either remote or local).
We need to define a behaviour for PHP files, telling nginx how to process them:
{{ filename(body="$CONFDIR/php.conf") }}
```nginx
location ~ ^(.+\.php)(.*)$ {
include fastcgi_params;
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}
```
This file specifies how files with a `.php` extension will be processed. Nginx
will split the arguments and filename, and pass them to the PHP-FPM socket,
which here is listening on the Unix socket at `/run/php-fpm/php-fpm.sock`. For a
TCP socket, the line 3 would need to be changed to something like this:
{{ filename(body="$CONFDIR/php.conf - TCP socket") }}
```nginx
location ~ ^(.+\.php)(.*)$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}
```
Next, to define a vhost hosting some PHP scripts, we simply need to include this
file:
{{ filename(body="$CONFDIR/vhosts/php.conf") }}
```nginx
server {
listen 0.0.0.0;
server_name my-awesome-php-app.enoent.fr;
access_log /var/log/nginx/my-awesome-php-app.access_log;
error_log /var/log/nginx/my-awesome-php-app.error_log info;
root /srv/http/localhost;
include php.conf;
}
```
### Rack
Rack-based applications need [Passenger](https://www.phusionpassenger.com/) to
work. Passenger is pretty similar to PHP-FPM, but its configuration with nginx
is easier. Note that it needs to be built in nginx.
To enable it, we need to tweak our `http` block in `$CONFDIR/nginx.conf` to
specify our Passenger root directory and path to the `ruby` executable:
{{ filename(body="$CONFDIR/nginx.conf") }}
```nginx
worker_processes auto;
events {
worker_connections 1024;
}
http {
proxy_send_timeout 600s;
proxy_read_timeout 600s;
fastcgi_send_timeout 600s;
fastcgi_read_timeout 600s;
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 0;
gzip on;
index index.html index.htm;
client_max_body_size 2048m;
passenger_root /usr/lib/passenger;
passenger_ruby /usr/bin/ruby;
}
```
Once this is done, to set up a Rack vhost, we just need to enable Passenger on
it, and define which environment we want to use for Rails applications:
{{ filename(body="$CONFDIR/vhosts/rack.conf") }}
```nginx
server {
listen 0.0.0.0;
server_name rack-app.enoent.fr;
access_log /var/log/nginx/rack-app.access_log;
error_log /var/log/nginx/rack-app.error_log info;
root /srv/http/rack-app/public;
passenger_enabled on;
rails_env production;
}
```
Note that the directory set as `root` must match the `public` directory of your
Rack application.
### Using all of these templates
Once we have written our vhosts definition files in `$CONFDIR/vhosts`, enabling
or disabling one is really easy. We just need to include the corresponding file
in the `http` block of our `$CONFDIR/nginx.conf` file:
{{ filename(body="$CONFDIR/nginx.conf") }}
```nginx
worker_processes auto;
events {
worker_connections 1024;
}
http {
proxy_send_timeout 600s;
proxy_read_timeout 600s;
fastcgi_send_timeout 600s;
fastcgi_read_timeout 600s;
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 0;
gzip on;
index index.html index.htm;
client_max_body_size 2048m;
passenger_root /usr/lib/passenger;
passenger_ruby /usr/bin/ruby;
include vhosts/static_html.conf;
include vhosts/reverse_proxy.conf;
include vhosts/php.conf;
include vhosts/rack.conf;
}
```
Obviously, if we don't include any Rack vhost, we don't need the lines 20 and
21 as they are Passenger-specific.
We can name our vhosts files whatever we like, and create as many as we need.
Having the general configuration split in reusable files allows an easy
maintenance. When deploying a new PHP application, we just need to include
`php.conf`, and not think "where is my PHP-FPM listening again?". It just works.