VERSION

Version 0.47

CONFIGURATION

CSRF Protection

VWF automatically issues a CSRF token as a cookie on every page load so that forms can protect against cross-site request forgery. The token is signed with an HMAC secret. You must supply that secret in your site's XML configuration file.

Add the following block to your domain's XML config (e.g. conf/example.com/config.xml):

<security>
  <csrf>
    <secret>your-long-random-string-here</secret>
  </csrf>
</security>

The secret can be any string, but should be long and unpredictable. Generate a good one with:

perl -MCrypt::URandom=urandom -e 'print unpack("H*", urandom(32)), "\n"'

or

openssl rand -hex 32

Tokens signed with this secret survive across FastCGI process restarts, so users will not lose form state when the server is reloaded.

What happens if you do not configure a secret

If security.csrf.secret is absent, VWF does not refuse to start. Instead it:

  1. Generates a cryptographically random 256-bit secret the first time a CSRF token is needed in the current process.

  2. Emits a one-time warning to the error log:

    VWF: security.csrf.secret is not configured; using a per-process
    random secret. CSRF tokens will not survive process restarts.
    Set security.csrf.secret in your site config to suppress this warning.
  3. Uses that random secret for every token issued in this process lifetime.

This means CSRF protection is still cryptographically strong - there is no hardcoded or guessable key - but if the FastCGI process restarts (e.g. on deploy or crash) any tokens issued before the restart become invalid. Users who had a form open will see a CSRF validation failure on submit and will need to reload the page.

To silence the warning and avoid that edge case, set the secret in config as shown above.

Disabling CSRF entirely

Set security.csrf.enable to 0 in your config if you do not use server-side form handling and do not need CSRF tokens at all:

<security>
  <csrf>
    <enable>0</enable>
  </csrf>
</security>

Safely set cookie values with validation.

Takes either a hash reference or a list of key-value pairs as input. Iterates over the parameters and stores them in the object's _cookies hash. Returns the object itself, allowing for method chaining.

add_preload

$self->add_preload($href, $as, %opts);

Queue a resource to be advertised to the browser (and to the HTTP/2 server) as a preload hint, emitted as a Link: rel=preload HTTP header.

In HTTP/2, the web server can read these headers and push the named assets to the client before the browser has finished parsing the HTML and discovered them itself. This eliminates a full round-trip for render-critical resources such as stylesheets and fonts, measurably reducing time-to-first-paint.

In HTTP/1.1 the header still provides a useful hint: browsers begin fetching the asset as soon as they see the response headers, in parallel with HTML parsing, which is faster than waiting for the parser to encounter the <link> or <script> tag.

Parameters

$href (required)

Root-relative path to the resource, e.g. /css/main.css. Must start with a /. Absolute URLs and relative paths are rejected to prevent header injection.

$as (required)

The W3C resource type. Must be one of:

audio  document  embed  fetch  font  image  object
script  style  track  video  worker

Choosing the correct type matters: it determines the request's priority, the Accept header the browser sends, and whether the resource is subject to Content Security Policy checks. An incorrect type causes the browser to ignore the hint silently.

crossorigin => 1 (optional)

Include the crossorigin attribute on the Link header. This is required for any resource that will be fetched in CORS anonymous mode - most notably web fonts, even when they are served from the same origin. Without it the browser issues a second, uncached fetch when it encounters the <link> tag in the HTML.

Defaults to 1 automatically when $as is font; for all other types defaults to 0.

Returns $self so calls may be chained.

When to call it

Call add_preload from a page subclass constructor, after $class->SUPER::new(@args) returns and before as_string is called. http() reads the preload queue when it runs, so any call made before that point will be included in the response.

Do not call it from html(): by the time html() executes, http() has already emitted the headers and the Link headers will be lost.

What to preload

Preload only resources that are render-critical for the current page - assets the browser will definitely need within the first few seconds of rendering. Good candidates:

  • The primary stylesheet (as=style)

  • Web fonts referenced by that stylesheet (as=font)

  • A critical above-the-fold script (as=script)

Avoid preloading everything: unused preloads waste bandwidth and compete with resources the browser has already prioritised. Per-page subclasses are the right place because each page knows exactly which assets its template needs.

Examples

Typical use inside a page subclass:

package Geo::Coder::Free::Display::index;
use parent 'Geo::Coder::Free::Display';

sub new {
    my ($class, @args) = @_;
    my $self = $class->SUPER::new(@args);
    return unless defined $self;   # blocked by Display (e.g. bad Referer)

    # Register render-critical assets for this page.
    # add_preload() returns $self so calls chain naturally.
    $self->add_preload('/css/main.css',         'style')
         ->add_preload('/fonts/body.woff2',     'font')    # crossorigin added automatically
         ->add_preload('/fonts/heading.woff2',  'font')
         ->add_preload('/js/index.js',          'script');

    return $self;
}

These produce the following headers in the HTTP response:

Link: </css/main.css>; rel=preload; as=style
Link: </fonts/body.woff2>; rel=preload; as=font; crossorigin
Link: </fonts/heading.woff2>; rel=preload; as=font; crossorigin
Link: </js/index.js>; rel=preload; as=script

A resource fetched via fetch() or XMLHttpRequest that requires CORS:

$self->add_preload('/api/config.json', 'fetch', crossorigin => 1);

An above-the-fold hero image (no crossorigin needed for images):

$self->add_preload('/img/hero.webp', 'image');

Why the base class preloads nothing by default

Geo::Coder::Free::Display ships no assets of its own, so there is nothing framework-level to preload. Additionally, http() runs before html(), which means the template has not yet been processed when the headers are emitted - the base class has no way to inspect template contents to discover asset references automatically. Each subclass is therefore responsible for declaring its own dependencies explicitly.

http

Returns the HTTP header section, terminated by an empty line