Placing Static Files in the Root Directory for Halo

Halo is built on Spring Boot, and its static asset resolution follows the conventions of Spring Web MVC. In a default setup, the framework may look in locations such as /static, /public, /resources, or META-INF/resources. Themes and plugins can also contribute assets through their own directories.

That means many Halo users place CSS, JavaScript, and images inside a theme folder or plugin folder. This is convenient, but it is not always the final answer. If you need a file to be reachable directly at the root, such as https://yourdomain.com/logo.png, the best approach may be to move static delivery to a web server.

How Halo Handles Static Resources

Halo separates static content from dynamic content. Dynamic content includes posts, pages, comments, and API responses. Static content includes stylesheets, scripts, images, and other assets.

When a request comes in, Spring Boot checks its static resource locations. If the file is found, it can be returned directly. If not, the request may be handled by the application logic. Halo also allows themes and plugins to register static resources.

However, relying on internal configuration to place files at the root is often not ideal. The framework may reserve certain paths for its own routes. As a result, a web server layer is usually more flexible.

The strongest solution is to let Nginx serve static files before forwarding unmatched requests to Halo.

Prepare the Static Directory

Create a folder for static assets. For example, use /usr/share/halo-static.

sudo mkdir -p /usr/share/halo-static
sudo cp /path/to/logo.png /usr/share/halo-static/

Configure Nginx

A basic server block can map the root location to the static directory.

server {
    listen 80;
    server_name example.com;

    root /usr/share/halo-static;

    location / {
        try_files $uri $uri/ @halo;
    }

    location @halo {
        proxy_pass http://127.0.0.1:8090;
        proxy_set_header Host $host;
        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;
    }
}

The try_files directive checks whether a file exists for the requested URI. If it does, Nginx returns the file. If it does not, Nginx forwards the request to the @halo location and eventually to Halo.

Add Cache Rules

Static assets often change rarely, so long cache periods make sense.

location ~* \.(jpg|jpeg|png|gif|css|js|ico)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

Alternative: Theme or Plugin Static Resources

A theme can expose static files through a static folder. For example, a custom file might live at themes/your-theme/static/custom-file.txt. The resulting URL might be /themes/your-theme/static/custom-file.txt. This is useful, but it does not place the file directly at the root.

A plugin can also register a resource handler.

@Configuration
public class StaticResourceConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/custom-static/**")
                .addResourceLocations("file:/usr/share/halo-custom-static/");
    }
}

This gives a custom prefix, but it may not solve root-level access by itself.

Important Considerations

When serving static files from the root, keep several points in mind.

  • Security: Avoid storing sensitive files in the public static directory.
  • Caching: Set appropriate headers for images, stylesheets, and scripts.
  • Conflicts: Do not use names that collide with reserved routes such as /admin or /api.
  • Docker: Mount the static directory outside the Halo container and let Nginx read it.

Conclusion

For static files that must live at the root in Halo, Nginx with try_files is the recommended approach. It provides fast static delivery, clean fallback behavior, and better separation from dynamic application logic. With the right proxy configuration, you can serve root-level assets while keeping