# How to Configure Nginx Reverse Proxy for the GeoLibre Web Build

> Easily configure your Nginx reverse proxy for the GeoLibre web build. Our template handles deep linking and API proxying, ensuring a smooth deployment.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-07-29

---

**GeoLibre provides a production-ready Nginx template in [`docker/nginx.conf`](https://github.com/opengeos/GeoLibre/blob/main/docker/nginx.conf) that serves the compiled SPA from the `dist/` folder, supports deep linking via `try_files`, and optionally proxies `/api/` requests to a FastAPI backend using environment variable substitution at container startup.**

Configuring an Nginx reverse proxy for the GeoLibre web build ensures your single-page application handles deep links correctly while maintaining the flexibility to proxy API calls to the Python FastAPI sidecar. The opengeos/GeoLibre repository ships a Dockerized Nginx setup that uses template rendering to inject runtime variables without requiring image rebuilds. This guide walks through the configuration structure, key directives, and deployment workflow based on the actual source implementation.

## Understanding the Nginx Template Architecture

The reverse proxy configuration is not a static file but a template processed at container startup. This architecture allows the same Docker image to run in different environments (development, staging, production) without rebuilding.

### The Template File Location

The primary configuration template resides at **[`docker/nginx.conf`](https://github.com/opengeos/GeoLibre/blob/main/docker/nginx.conf)** in the repository root. During the Docker build, this file is copied into the image alongside the compiled web assets from `apps/geolibre-desktop/dist`. The Dockerfile places the template at `/etc/nginx/nginx.conf.template`, while the final rendered configuration is written to [`/etc/nginx/conf.d/default.conf`](https://github.com/opengeos/GeoLibre/blob/main//etc/nginx/conf.d/default.conf) at runtime.

### Runtime Variable Injection

The **[`docker/entrypoint.sh`](https://github.com/opengeos/GeoLibre/blob/main/docker/entrypoint.sh)** script handles the template rendering process. Before Nginx starts, this script substitutes environment variables (such as `${CSP}` and `${BACKEND_URL}`) into the template file. This mechanism allows you to modify security policies or backend endpoints via container environment variables without touching the source configuration.

## Core Reverse Proxy Components

The Nginx configuration handles three primary responsibilities: serving static assets, ensuring SPA routing works correctly, and optionally proxying API requests to the backend.

### Static Asset Serving and SPA Fallback

The web build output from `apps/geolibre-desktop/dist` is served from `/usr/share/nginx/html`. The critical directive for SPA support is the `try_files` statement:

```nginx
location / {
  root /usr/share/nginx/html;
  index index.html;
  try_files $uri $uri/ /index.html;
}

```

This configuration ensures that direct requests to routes like `/projects/123` fall back to [`index.html`](https://github.com/opengeos/GeoLibre/blob/main/index.html), allowing the client-side router to handle the navigation. Without this directive, Nginx would return 404 errors for deep links.

### API Proxy to FastAPI Sidecar

When running the optional Python FastAPI sidecar on port 8765, the reverse proxy forwards requests under the `/api/` path:

```nginx
location /api/ {
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_pass ${BACKEND_URL};
}

```

Set the `BACKEND_URL` environment variable (for example, `http://host.docker.internal:8765` or `http://api:8765` in a Docker Compose network) when starting the container to activate this proxying behavior.

### Health Check Endpoint

To support Docker's `HEALTHCHECK` command without triggering the SPA fallback logic, a dedicated endpoint returns a simple 200 response:

```nginx
location = /healthz {
  access_log off;
  return 200 'OK';
}

```

This endpoint bypasses the `try_files` directive and provides a lightweight way for orchestrators to verify that Nginx is alive and responsive.

## Security and Performance Headers

The template includes production-hardened defaults for security headers and compression.

### Content Security Policy Configuration

The configuration sets a strict Content-Security-Policy (CSP) that aligns with the values defined in [`apps/geolibre-desktop/vite.config.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/vite.config.ts). The template uses variable substitution for policy directives:

```nginx
add_header Content-Security-Policy "${CSP}" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;

```

Define the `CSP` environment variable to include your specific `connect-src`, `img-src`, and other directives required for your deployment environment (for example, allowing `http://localhost:*` for local development or your specific CDN domains).

### Compression Settings

Text-based assets are compressed automatically to improve load times:

```nginx
gzip on;
gzip_types text/plain text/css application/javascript image/svg+xml;

```

This reduces bandwidth usage for JavaScript bundles and CSS files served from the `dist` directory.

## Implementation Steps

Follow this workflow to deploy the GeoLibre web build with the Nginx reverse proxy:

1. **Build the web assets** using `npm run build` in the `geolibre-desktop` workspace to generate the `dist/` folder.
2. **Build the Docker image**, which copies both the built assets and the Nginx template ([`docker/nginx.conf`](https://github.com/opengeos/GeoLibre/blob/main/docker/nginx.conf)) into the image.
3. **Set environment variables** before running the container:
   - `CSP`: The full Content-Security-Policy string
   - `BACKEND_URL`: The URL for the FastAPI sidecar (optional)
4. **Start the container** using the entrypoint script, which renders the template and launches Nginx as PID 1.
5. **Access the application** at `http://localhost` (port 80), with all SPA routes and API proxying functional.

Example Docker run command:

```bash
docker run -p 80:80 \
  -e CSP="default-src 'self'; connect-src 'self' http://localhost:*" \
  -e BACKEND_URL="http://host.docker.internal:8765" \
  opengeos/geolibre:latest

```

## Summary

- The working configuration lives in **[`docker/nginx.conf`](https://github.com/opengeos/GeoLibre/blob/main/docker/nginx.conf)** and acts as a template for runtime variable substitution.
- **[`docker/entrypoint.sh`](https://github.com/opengeos/GeoLibre/blob/main/docker/entrypoint.sh)** renders the final Nginx configuration by injecting environment variables like `${CSP}` and `${BACKEND_URL}`.
- The **`try_files $uri $uri/ /index.html`** directive in the root location block enables proper SPA routing for deep links.
- API requests to `/api/` are proxied to the FastAPI sidecar when the `BACKEND_URL` variable is configured.
- Security headers and gzip compression are pre-configured in the template to meet production standards.

## Frequently Asked Questions

### Where is the Nginx configuration file located in the repository?

The source template is located at **[`docker/nginx.conf`](https://github.com/opengeos/GeoLibre/blob/main/docker/nginx.conf)** in the opengeos/GeoLibre repository. When the Docker image builds, this file is copied to `/etc/nginx/nginx.conf.template` inside the container. At runtime, [`docker/entrypoint.sh`](https://github.com/opengeos/GeoLibre/blob/main/docker/entrypoint.sh) processes this template and writes the rendered output to [`/etc/nginx/conf.d/default.conf`](https://github.com/opengeos/GeoLibre/blob/main//etc/nginx/conf.d/default.conf) before starting the Nginx process.

### How does the configuration handle client-side routing in the GeoLibre SPA?

The configuration uses the **`try_files $uri $uri/ /index.html`** directive within the root location block. When a user accesses a deep link like `/projects/123`, Nginx first attempts to serve the file or directory literally. If neither exists, it falls back to serving [`index.html`](https://github.com/opengeos/GeoLibre/blob/main/index.html), allowing the React or Vue router to handle the client-side navigation and render the correct view.

### Can I proxy requests to a backend API without modifying the nginx.conf file?

Yes. The template supports environment variable substitution for the `BACKEND_URL`. Set the `BACKEND_URL` environment variable (for example, to `http://api-service:8765`) when starting the container. The entrypoint script automatically substitutes this value into the `proxy_pass` directive within the `/api/` location block, enabling reverse proxying without rebuilding the image.

### What is the purpose of the `/healthz` endpoint?

The **`location = /healthz`** block provides a lightweight health check endpoint that returns HTTP 200 with the body "OK". This endpoint is exempt from the SPA fallback logic and access logging, making it ideal for Docker `HEALTHCHECK` commands or Kubernetes liveness probes to verify that the Nginx container is running and responsive without triggering application logic or cluttering logs.