Skip to main content
Skip to main content
🚧 Work in Progress

Static Service

The Static service provides a way to serve static content through a pre-configured Nginx setup. It balances simplicity with the flexibility needed for modern web applications.

Experience the simplicity of Zerops

Deploy an Analog app with static hosting in seconds. All you need is a Zerops account.

Quick Start​

Add a Static service to your project by including this in your zerops.yaml:

zerops.yaml
zerops:
- setup: app
run:
os: alpine
base: static

Default Behavior​

Every Static service in Zerops comes with built-in defaults optimized for modern web applications, including Single Page Applications. By default, for any incoming request, the service will:

  1. Try to serve the exact path ($uri)
  2. Try with .html extension ($uri.html)
  3. Look for an index.html in the directory ($uri/index.html)
  4. Fall back to /index.html (suitable for SPAs)
  5. Return 404 if none of the above exist
SPAs

Single Page Applications work out of the box without any additional configuration. The built-in fallback to /index.html ensures that client-side routing functions properly.

Routing Configuration​

The Static service allows you to configure additional URL routing and redirects through simple YAML configuration, abstracting away the complexity of Nginx configuration.

Custom Routing Configuration​

Configure custom routing beyond the default behavior in the run.routing section of your zerops.yaml:

zerops.yaml
run:
routing:
redirects:
# Only needed for custom redirect scenarios
- from: /special-path/*
to: /specific-landing-page
status: 302

Redirect Types​

Relative Redirects​

Note

Remember that SPA routing is already built into the default behavior. You don't need to add any custom redirects for client-side routing to work.

Use relative redirects to route paths within your application. When both from and to are relative paths, you can omit the status code to create a masked redirect that shows the content of the target page while preserving the original URL:

zerops.yaml
routing:
redirects:
# Masked redirect - URL stays the same but shows content from about-us
- from: /about
to: /about-us

# Standard redirect with status code
- from: /old-page
to: /new-page
status: 301

# Preserve the path when redirecting between directories
- from: /blog/*
to: /articles/
preservePath: true
status: 302

# Preserve both path and query parameters
- from: /posts/*
to: /blog/
preservePath: true
preserveQuery: true
status: 302
Important

When using preservePath with wildcards, ensure the to path ends with a / to maintain proper path concatenation. For example, /blog/* to /new-blog/ will correctly redirect /blog/hello.html to /new-blog/hello.html, while /new-blog would result in /new-bloghello.html.

Absolute Redirects​

For redirecting between domains or to external URLs, use absolute redirects by including http:// or https://. When using absolute URLs in either from or to, you must specify a status code:

zerops.yaml
routing:
redirects:
# Redirect an old domain to a new one
- from: https://old-domain.com/*
to: https://new-domain.com
status: 301
preserveQuery: true # Optional: maintain query parameters

# Redirect with path preservation
- from: https://old-site.com/*
to: https://new-site.com/
status: 301
preservePath: true

Advanced Routing Features​

Wildcard Matching​

Use * as a wildcard in your paths:

  • At the end of a path: Matches any subsequent content
  • At the start of a domain (after https://): Enables regex matching for subdomains

Example of domain management:

zerops.yaml
run:
routing:
redirects:
# Redirect a specific domain to an article
- from: https://promo-domain.com/*
to: https://main-site.com/special-offer
status: 302

# Redirect all subdomains to main site
- from: https://*.old-domain.com/*
to: https://main-site.com
status: 302

Matching Priority​

When multiple redirects are configured, they follow Nginx's matching priority system:

  1. Exact matches are checked first
  2. Simple path matches (without wildcards) are checked next
  3. Pattern matches (with wildcards) are checked last

For example:

zerops.yaml
routing:
redirects:
# Exact match for homepage - standard redirect
- from: /
to: /home
status: 302

# Simple path match - masked redirect
- from: /about
to: /about-us

# Pattern match with path preservation
- from: /blog/*
to: /articles/
preservePath: true
status: 302

In this configuration:

  • / will redirect to /home with a 302 status
  • /about will show content from /about-us but keep the URL as /about
  • /blog/post-123.html will redirect to /articles/post-123.html
  • Any other path will use the default behavior

CORS Configuration​

You can enable CORS for your static service by adding a cors directive:

zerops.yaml
run:
routing:
# Simple case - automatically converted to '*'
cors: "*"

# Full syntax with proper quoting
cors: "'*' always"

The cors directive sets the following headers:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Expose-Headers
Note

The cors directive has a special case: if you specify just "*", it's automatically converted to '*'. For any other values, you need to include the proper Nginx syntax including quotes.

Custom Headers​

For more control over HTTP headers, use the headers directive:

zerops.yaml
run:
routing:
headers:
- for: "/"
values:
# All values need proper quoting since they're inserted directly into Nginx
X-Frame-Options: "'DENY'"

# Values with internal quotes need proper YAML escaping
Content-Security-Policy: '"default-src ''self''"'
important

Header values are inserted directly into the Nginx configuration without additional quotes, which means:

  1. All values must include their own quotes (typically single quotes)
  2. If you need single quotes inside your header value, you must escape them in YAML (using double single quotes)
  3. To include the always directive, add it after your quoted value
  4. For complex values, you can use YAML's block scalar notation (>-) for better readability

Here are examples for different header scenarios:

zerops.yaml
headers:
- for: "/"
values:
# Simple header with proper quoting
X-Frame-Options: "'DENY'"

# Header with 'always' directive
X-XSS-Protection: "'1; mode=block' always"

# Header with internal single quotes - need double single quotes for escaping
Content-Security-Policy: '"default-src ''self'' https://cdn.example.com"'

# Complex header with block scalar notation for better readability
Content-Security-Policy: >-
"default-src 'self' https://cdn.example.com;
script-src 'self' 'unsafe-inline';
img-src * data:" always

When this configuration is processed, it translates to the following Nginx directives:

add_header X-Frame-Options 'DENY';
add_header X-XSS-Protection '1; mode=block' always;
add_header Content-Security-Policy "default-src 'self' https://cdn.example.com";
add_header Content-Security-Policy "default-src 'self' https://cdn.example.com; script-src 'self' 'unsafe-inline'; img-src * data:" always;
Path Handling

When you specify headers for a path that doesn't have an existing location block, the Static service automatically creates a location with the same default behavior as the root path (trying files in order: $uri, $uri.html, $uri/index.html, /index.html or returning 404).

If you add headers for a path that already has a location block, your headers will be merged with the existing configuration.

Combining CORS and Custom Headers​

You can use both CORS and custom headers together:

zerops.yaml
run:
routing:
cors: "'*' always"
headers:
- for: "/"
values:
X-Frame-Options: "'DENY'"

The cors directive sets default Access-Control headers for all routes, while the headers directive allows you to set additional headers for specific paths.

important

If you specify Access-Control headers in the headers directive, they will override the ones set by cors for that specific path.

Prerender Integration​

The Static service includes built-in support for Prerender.io for server-side rendering for search engines and social media crawlers.

Basic Prerender Setup​

  1. Set the PRERENDER_TOKEN secret variable with your Prerender.io token
  2. The service automatically configures necessary rewrites based on user agents

Custom Prerender Host​

If you're using a custom Prerender host, add it to environment variables in zerops.yaml:

zerops.yaml
run:
envVariables:
- PRERENDER_HOST=your.prerender.host
Default

The default host is service.prerender.io if not specified.

Advanced Configuration​

Switching to Full Nginx​

If you need more control over your Nginx configuration:

  1. Go to your Static service overview in the UI
  2. Click the three vertical dots in the left panel
  3. Select Need to switch to full Nginx service?
  4. Copy the generated Nginx configuration
  5. Use this configuration as a starting point for a full Nginx service
Tip

This allows you to move to a more customizable setup while maintaining your existing routing logic.

Best Practices​

  1. Domain Migration

    zerops.yaml
    routing:
    redirects:
    - from: https://old-domain.com/*
    to: https://new-domain.com
    status: 301

    Use permanent (301) redirects when permanently moving content to maintain SEO value.

  2. Complex Redirects

    zerops.yaml
    routing:
    redirects:
    - from: /specific-path/*
    to: /specific-landing
    status: 302
    # Additional specific redirects go here
  3. Security Headers Add security headers to protect your application:

    zerops.yaml
    routing:
    headers:
    - for: "/*"
    values:
    X-Frame-Options: "'DENY'"
    X-Content-Type-Options: "'nosniff'"
    # Note the proper quoting for values with single quotes
    Content-Security-Policy: '"default-src ''self''"'

Frontend Framework Integration​

The Static service works with modern frontend frameworks. It can serve built static files from any framework with options for custom routing and Prerender.io integration if needed.

Example: Analog App Deployment​

Here's a configuration for deploying an Analog application:

zerops.yaml
zerops:
- setup: app
build:
base: nodejs@20
buildCommands:
- pnpm i
- pnpm build
deployFiles:
- dist/analog/public/~
run:
base: static

This configuration:

  1. Uses Node.js 20 for building the application
  2. Installs dependencies with pnpm
  3. Builds the application
  4. Deploys the resulting static files to the Static service

You can enhance this basic setup with:

  • Custom redirects for URL management
  • Prerender.io integration for SEO
  • Additional routing rules as needed

Common Configurations​

Multiple Domain Management​

zerops.yaml
run:
routing:
redirects:
# Product-specific domain
- from: https://product-promo.com/*
to: https://main-site.com/products
status: 302

# Campaign domain
- from: https://special-offer.com/*
to: https://main-site.com/campaign
status: 302

# Legacy domains and subdomains
- from: https://*.legacy-domain.com/*
to: https://main-site.com
status: 302

Development Setup​

zerops.yaml
run:
routing:
# CORS with proper quoting
cors: "'*' always"
redirects:
# API requests
- from: /api/*
to: https://api.your-domain.com
status: 302

Security-Enhanced Configuration​

zerops.yaml
run:
routing:
headers:
# Custom headers for default location
- for: "/*"
values:
X-Frame-Options: "'DENY' always"
X-Content-Type-Options: "'nosniff' always"
# Note the proper escaping of single quotes
Content-Security-Policy: '"default-src ''self''" always'