2023-07-02 20:31:04 +00:00
|
|
|
# How to use a reverse proxy
|
|
|
|
|
|
|
|
<!-- markdownlint-disable proper-names -->
|
|
|
|
|
|
|
|
When having multiple webservers or other applications running, that also use
|
|
|
|
port 80 and 443, you probably want to use a reverse proxy to serve HedgeDoc.
|
|
|
|
|
|
|
|
We'll assume the domain you use for the instance is <https://md.example.com>, so please
|
|
|
|
substitute your actual domain anywhere you encounter <https://md.example.com>.
|
|
|
|
|
|
|
|
## Configuring the reverse proxy
|
|
|
|
|
|
|
|
We have collected some example configurations for popular reverse proxies below.
|
|
|
|
At the end is also a list of generic things the reverse proxy must do, if you prefer
|
|
|
|
to write your own config or use a reverse proxy not mentioned here.
|
|
|
|
|
|
|
|
### Traefik
|
|
|
|
|
|
|
|
As [traefik][traefik] has direct access to your running Docker containers, there is no need to
|
|
|
|
configure extra ports. Instead, you'll only have to add the following labels to the services
|
|
|
|
in your `docker-compose.yml`:
|
|
|
|
|
|
|
|
<!-- markdownlint-disable line-length no-space-in-code -->
|
|
|
|
|
|
|
|
??? abstract "docker-compose.yml"
|
|
|
|
```yaml
|
|
|
|
backend:
|
2023-10-23 20:46:19 +00:00
|
|
|
image: ghcr.io/hedgedoc/hedgedoc/backend:2.0.0-alpha.2
|
2023-07-02 20:31:04 +00:00
|
|
|
volumes:
|
|
|
|
- $PWD/.env:/usr/src/app/backend/.env
|
|
|
|
- hedgedoc_uploads:/usr/src/app/backend/uploads
|
|
|
|
labels:
|
|
|
|
traefik.enable: "true"
|
|
|
|
traefik.http.routers.hedgedoc_2_backend.rule: "Host(`md.example.com`) && PathPrefix(`/realtime`, `/api`, `/public`)"
|
|
|
|
traefik.http.routers.hedgedoc_2_backend.tls: "true"
|
|
|
|
traefik.http.routers.hedgedoc_2_backend.tls.certresolver: "letsencrypt"
|
|
|
|
traefik.http.services.hedgedoc_2_backend.loadbalancer.server.port: "3000"
|
|
|
|
traefik.http.services.hedgedoc_2_backend.loadbalancer.server.scheme: "http"
|
|
|
|
frontend:
|
2023-10-23 20:46:19 +00:00
|
|
|
image: ghcr.io/hedgedoc/hedgedoc/frontend:2.0.0-alpha.2
|
2023-07-02 20:31:04 +00:00
|
|
|
environment:
|
|
|
|
HD_BASE_URL: "${HD_BASE_URL}"
|
|
|
|
labels:
|
|
|
|
traefik.enable: "true"
|
|
|
|
traefik.http.routers.hedgedoc_2_frontend.rule: "Host(`md.example.com`)"
|
|
|
|
traefik.http.routers.hedgedoc_2_frontend.tls: "true"
|
|
|
|
traefik.http.routers.hedgedoc_2_frontend.tls.certresolver: "letsencrypt"
|
|
|
|
traefik.http.services.hedgedoc_2_frontend.loadbalancer.server.port: "3001"
|
|
|
|
traefik.http.services.hedgedoc_2_frontend.loadbalancer.server.scheme: "http"
|
|
|
|
```
|
|
|
|
|
|
|
|
<!-- markdownlint-enable line-length no-space-in-code -->
|
|
|
|
|
|
|
|
We added [Let's Encrypt][letsencrypt] as a certificate resolver, as it enables you to
|
|
|
|
quickly use HTTPS. If you don't want to use that feel free to change
|
|
|
|
the `.certresolver` variables accordingly.
|
|
|
|
|
|
|
|
If you used the `docker-compose.yml` file from the tutorial, please remove
|
|
|
|
the service `proxy` and the volume `caddy_data` as caddy is no longer needed when using traefik.
|
|
|
|
|
|
|
|
### Other reverse proxies
|
|
|
|
|
|
|
|
In the following we'll also assume that you run a HedgeDoc backend on port `3000`,
|
|
|
|
a HedgeDoc frontend on port `3001`.
|
|
|
|
Furthermore, we assume that you have TLS certificates located at
|
|
|
|
`/etc/letsencrypt/live/md.example.com/fullchain.pem`
|
|
|
|
and
|
|
|
|
`/etc/letsencrypt/live/md.example.com/privkey.pem` respectively
|
|
|
|
and are using [Let's Encrypt][letsencrypt] for your certificates.
|
|
|
|
Replace these paths with the actual paths to your certificates.
|
|
|
|
|
|
|
|
**Preparations when using the default docker-compose.yml:**
|
|
|
|
|
|
|
|
If your starting with the `docker-compose.yml` file from the tutorial,
|
|
|
|
you need to add the `ports` entry for both `backend` and `frontend` as following.
|
|
|
|
|
|
|
|
<!-- markdownlint-disable no-space-in-code -->
|
|
|
|
|
|
|
|
??? abstract "docker-compose.yml"
|
|
|
|
```yaml
|
|
|
|
backend:
|
2023-10-23 20:46:19 +00:00
|
|
|
image: ghcr.io/hedgedoc/hedgedoc/backend:2.0.0-alpha.2
|
2023-07-02 20:31:04 +00:00
|
|
|
volumes:
|
|
|
|
- $PWD/.env:/usr/src/app/backend/.env
|
|
|
|
- hedgedoc_uploads:/usr/src/app/backend/uploads
|
|
|
|
ports:
|
|
|
|
- "3000:3000"
|
|
|
|
frontend:
|
2023-10-23 20:46:19 +00:00
|
|
|
image: ghcr.io/hedgedoc/hedgedoc/frontend:2.0.0-alpha.2
|
2023-07-02 20:31:04 +00:00
|
|
|
environment:
|
|
|
|
HD_BASE_URL: "${HD_BASE_URL}"
|
|
|
|
ports:
|
|
|
|
- "3001:3001"
|
|
|
|
```
|
|
|
|
|
|
|
|
<!-- markdownlint-enable no-space-in-code -->
|
|
|
|
|
|
|
|
Also, you want to remove the service `proxy` and the volume `caddy_data`
|
|
|
|
to avoid port conflicts with your reverse-proxy software.
|
|
|
|
|
|
|
|
#### nginx
|
|
|
|
|
|
|
|
Here is an example configuration for [nginx][nginx].
|
|
|
|
|
|
|
|
<!-- markdownlint-disable code-block-style -->
|
|
|
|
|
|
|
|
??? abstract "nginx config"
|
|
|
|
```
|
|
|
|
map $http_upgrade $connection_upgrade {
|
|
|
|
default upgrade;
|
|
|
|
'' close;
|
|
|
|
}
|
|
|
|
server {
|
|
|
|
server_name md.example.com;
|
|
|
|
|
|
|
|
location ~ ^/(api|public|uploads|apidoc)/ {
|
|
|
|
proxy_pass http://127.0.0.1:3000;
|
2023-12-14 17:46:21 +00:00
|
|
|
proxy_set_header X-Forwarded-Host $host;
|
2023-07-02 20:31:04 +00:00
|
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
}
|
|
|
|
|
|
|
|
location /realtime {
|
|
|
|
proxy_pass http://127.0.0.1:3000;
|
2023-12-14 17:46:21 +00:00
|
|
|
proxy_set_header X-Forwarded-Host $host;
|
2023-07-02 20:31:04 +00:00
|
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
|
|
proxy_set_header Connection $connection_upgrade;
|
|
|
|
}
|
|
|
|
|
|
|
|
location / {
|
|
|
|
proxy_pass http://127.0.0.1:3001;
|
2023-12-14 17:46:21 +00:00
|
|
|
proxy_set_header X-Forwarded-Host $host;
|
2023-07-02 20:31:04 +00:00
|
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
}
|
|
|
|
|
|
|
|
listen [::]:443 ssl http2;
|
|
|
|
listen 443 ssl http2;
|
|
|
|
ssl_certificate fullchain.pem;
|
|
|
|
ssl_certificate_key privkey.pem;
|
|
|
|
include options-ssl-nginx.conf;
|
|
|
|
ssl_dhparam ssl-dhparams.pem;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
<!-- markdownlint-disable code-block-style -->
|
|
|
|
|
|
|
|
#### Apache
|
|
|
|
|
|
|
|
You will need these modules enabled: `proxy`, `proxy_http` and `proxy_wstunnel`.
|
|
|
|
Here is an example config snippet for [Apache][apache]:
|
|
|
|
|
|
|
|
<!-- markdownlint-disable code-block-style -->
|
|
|
|
|
|
|
|
??? abstract "Apache config"
|
|
|
|
```
|
|
|
|
<VirtualHost *:443>
|
|
|
|
ServerName md.example.com
|
|
|
|
|
|
|
|
RewriteEngine on
|
|
|
|
RewriteCond %{REQUEST_URI} ^/realtime [NC]
|
|
|
|
RewriteCond %{HTTP:Upgrade} =websocket [NC]
|
|
|
|
RewriteRule /(.*) ws://127.0.0.1:3000/$1 [P,L]
|
|
|
|
|
|
|
|
ProxyPass /api http://127.0.0.1:3000/
|
|
|
|
ProxyPass /apidoc http://127.0.0.1:3000/
|
|
|
|
ProxyPass /public http://127.0.0.1:3000/
|
|
|
|
ProxyPass /realtime http://127.0.0.1:3000/
|
|
|
|
|
|
|
|
ProxyPassReverse /api http://127.0.0.1:3000/
|
|
|
|
ProxyPassReverse /apidoc http://127.0.0.1:3000/
|
|
|
|
ProxyPassReverse /public http://127.0.0.1:3000/
|
|
|
|
ProxyPassReverse /realtime http://127.0.0.1:3000/
|
|
|
|
|
|
|
|
ProxyPass / http://127.0.0.1:3001/
|
|
|
|
ProxyPassReverse / http://127.0.0.1:3001/
|
|
|
|
|
|
|
|
RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
|
|
|
|
|
|
|
|
ErrorLog ${APACHE_LOG_DIR}/error.log
|
|
|
|
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
|
|
|
|
|
|
|
SSLCertificateFile /etc/letsencrypt/live/md.example.com/fullchain.pem
|
|
|
|
SSLCertificateKeyFile /etc/letsencrypt/live/md.example.com/privkey.pem
|
|
|
|
Include /etc/letsencrypt/options-ssl-apache.conf
|
|
|
|
</VirtualHost>
|
|
|
|
```
|
|
|
|
|
|
|
|
<!-- markdownlint-enable code-block-style -->
|
|
|
|
|
|
|
|
#### Generic
|
|
|
|
|
|
|
|
Here is a list of things your reverse proxy needs to do to let HedgeDoc work:
|
|
|
|
|
|
|
|
- Websocket `Upgrade` requests at path `/realtime`.
|
|
|
|
- Passing `/realtime` to <http://localhost:3000>
|
|
|
|
- Passing `/api/*` to <http://localhost:3000>
|
|
|
|
- Passing `/public/*` to <http://localhost:3000>
|
|
|
|
- Passing `/uploads/*` to <http://localhost:3000>
|
|
|
|
- Passing `/apidoc/*` to <http://localhost:3000>
|
|
|
|
- Passing `/*` to <http://localhost:3001>
|
|
|
|
- Set the `X-Forwarded-Proto` header
|
|
|
|
|
|
|
|
In essence there are a few special urls that need to be handled by the HedgeDoc backend
|
|
|
|
and everything else is handled by the frontend.
|
|
|
|
|
|
|
|
<!-- markdownlint-enable proper-names -->
|
|
|
|
|
|
|
|
[traefik]: https://traefik.io/traefik/
|
|
|
|
[letsencrypt]: https://letsencrypt.org/
|
|
|
|
[nginx]: https://nginx.org/
|
|
|
|
[apache]: https://httpd.apache.org/
|