Roosevelt

Configuration — App behavior

🔝 Scroll to top

App behavior options

Default: [Object]

{
  urlEncoded: {
    extended: true
  },
  json: {}
}
  • csrfProtection [Boolean or Object]: Whether to enable Cross-Site Request Forgery protection. Roosevelt asks the browser where each request came from and refuses any request that changes data unless the browser says it came from your own site, which browsers report themselves and page scripts cannot forge. Default: true.
    • To disable the feature entirely, set it to false. To configure it, supply an object.
    • In the object:
      • blockCrossSiteRequests [Boolean]: Whether to ask the browser where each request came from and refuse any request that changes data unless the browser says it came from your own site. Default: true.
        • Requests that arrive without the browser saying where they came from are also refused. Anything that is not a browser, such as a mobile app or another server, does not send that information, so add those routes to exemptions.
        • This cannot stop an untrusted request coming from another subdomain of your own site, because browsers report those the same way they report your own pages. Use CSRF tokens via requireTokens to add protection against untrusted subdomains.
        • Setting this param to false leaves your app protected only by whatever requireTokens is set to.
      • requireTokens [Boolean or String]: Whether requests that change data must carry a CSRF token. Default: false.
        • false: no token is needed, which is fine for most apps. Note that req.csrfToken() is not available when set to false, since there are no tokens to generate.
        • true: always require a token. Set to true if anything untrusted is hosted on a subdomain you share, such as user uploaded content or a separate app someone else runs. That is the only way to stop an untrusted request coming from another subdomain of your own site, because browsers report those the same way they report your own pages. See the coding apps section for examples.
        • "whenHeaderMissing": require a token only from browsers too old to report where a request came from, since they are otherwise refused. This does not stop an untrusted request coming from another subdomain of your own site either, because browsers that do report where a request came from are trusted without a token.
      • trustedOrigins [Array of Strings]: Other sites you expect requests from, such as a payment provider posting back to your app. Default: [].
        • Example: ["https://accounts.someplace.somedomain"]. Matched against the request's Origin header.
        • This only helps callers that send an Origin header, which means browsers. Use exemptions for anything else.
      • exemptions [Array of Strings]: Routes to skip all of these checks on. Supports wildcard matching. Use this for routes called by anything that is not a browser, such as a mobile app or another server.

Example of exemptions list: [Object]

{
  exemptions: [
    '/foo',
    '/bar',
    '/baz'
  ]
}
  • expressSession [Boolean or Object]: Parameters to pass to the express-session module. Default: true.

Default if expressSession is set to true: [Object]

{
  secret, // an auto-generated secret, read from your secrets folder
  resave: false, // usually a bad idea to set to true
  saveUninitialized: false, // usually a bad idea to set to true
  cookie: {
    secure: 'auto', // marks the cookie HTTPS only on any request that reached your app over HTTPS, whether the app served it or a web server in front did
    sameSite: 'strict', // adds same site enforcement
    maxAge: 347126472000 // sets expiration very far in the future (~11 years) to basically never expire
  },
  store // the expressSessionStore.instance Roosevelt param
}

If you supply your own config, note what cookie.sameSite does before leaving it out: Setting it to "strict" tells the browser not to send the session cookie along with any request that came from another site. A forged request from another site therefore arrives with nobody logged in, which is one of the protections that stops another site from making requests as your logged in users. Roosevelt warns at startup if your config leaves it unset, since a hand written config can drop it without meaning to.

It does not stop a request coming from another subdomain of your own site, because browsers count those as the same site. Only setting csrfProtection.requireTokens to true does that.

One side effect of "strict" is that a user following a link to your app from somewhere else, such as an email or a chat message, arrives without their session on that first page load and appears logged out. Reloading the page or clicking any link within your app restores it, since those requests come from your own site.

If that matters for your app, set cookie.sameSite to "lax" instead. The browser will then send the session cookie when a user navigates to your app from elsewhere, while still withholding it from a form or script on another site that tries to change data.

Whichever you choose, csrfProtection.blockCrossSiteRequests still refuses requests that change data unless the browser says they came from your own site, so relaxing this setting does not give up your CSRF protection.

Give users a new session when they log in. This applies to any app with logins, whichever session store it uses. A visitor usually has a session before they log in, from browsing your site. If your app logs them in by adding who they are to that same session, anyone who already knew its ID now has a logged-in session as that user. Someone could have learned or set the ID beforehand, for example on a shared computer, or by planting a cookie from another subdomain of your site. This is called session fixation. To prevent it, call req.session.regenerate() when a user logs in, which replaces their session with a new one under a new ID, and put who they are in the new one:

req.session.regenerate(err => {
  if (err) return next(err)
  req.session.userId = user.id
  req.session.save(err => err ? next(err) : res.redirect('/'))
})
  • expressSessionStore [Object]: Define a custom session store to use with express-session instead of the default one provided by Roosevelt. Roosevelt's default store keeps sessions in a file on the server running the app, so you need this if you run your app on more than one server. See storing sessions somewhere every server can reach.

    • filename [String]: Name of the session file.

    • instance: [Object] A store instance. See this list for compatible stores.

    • preset [String]: Available presets provided by Roosevelt. Only used if instance is not provided.

      • Available options:

        • "default": Use Roosevelt's default session store, which keeps sessions in a SQLite file on the server the app runs on, named by filename. It began as a hard fork of better-sqlite3-session-store, which is no longer maintained, and now shares its code with the postgres, mysql, and mariadb presets below, so each of them expires sessions the same way.

        • "postgres": Keep sessions in PostgreSQL. They are kept in the database given as presetOptions.client, or in app.get('db') if you set one in the onBeforeMiddleware event. They are kept in a sessions table Roosevelt makes the first time it is used. The database can be anything with a query(sql, params) method, such as a pg Pool.

          • Example, with a pool of its own:
        expressSessionStore: {
          preset: 'postgres',
          presetOptions: {
            client: new (require('pg').Pool)({ connectionString: process.env.DATABASE_URL }) // a pool does not connect until it is first used
          }
        }
          - Example, with your app's own database, set in `onBeforeMiddleware`, and closed in `onAppExit`:
        expressSessionStore: { preset: 'postgres' },
        onBeforeMiddleware: async app => app.set('db', await connectToMyDatabase()),
        onAppExit: app => app.get('db').end()
      - `"mysql"`: Keep sessions in MySQL, the same way as the `postgres` preset. The database can be anything with a `query(sql, params)` method, such as a [mysql2](https://sidorares.github.io/node-mysql2/) pool or a [mariadb](https://github.com/mariadb-corporation/mariadb-connector-nodejs) pool, whichever way it hands back its results.
      - `"mariadb"`: The same as `"mysql"`, for MariaDB. It clears out expired sessions and ones past `maxInactivity` the same way as the default store.
      - `"express-session-default"`: Use `express-session`'s own default store, which keeps sessions in memory. Not recommended: every session is lost when the process restarts, so a deploy signs everyone out, and memory use grows without bound. `express-session` itself advises against it outside development.
    • presetOptions [Object]: Options to pass to the preset session store if one is selected. Only used if instance is not provided.

      • checkPeriod [Number]: How often, in milliseconds, Roosevelt clears sessions that have gone past maxInactivity out of the session store.
      • client [Object]: For the postgres, mysql, and mariadb presets, the database to keep sessions in: anything with a query(sql, params) method, such as a connection pool from pg, mysql2, or mariadb. Without it, the preset uses app.get('db'), if your app sets it in its onBeforeMiddleware event.
      • table [String]: For the postgres, mysql, and mariadb presets, the table to keep sessions in. Default: "sessions".
    • Either instance or preset must be set for this param to work properly.

    • maxInactivity [Number]: How long, in milliseconds, a session may go unused before Roosevelt deletes it from the session store. Default: 7889238000 (about 3 months). Only applies to Roosevelt's default session store.

      • This is separate from expressSession.cookie.maxAge, which decides how long a user stays logged in. Roosevelt sets that very far in the future by default so that active users are never logged out, which would otherwise mean abandoned sessions sat in the session store for just as long. maxInactivity lets you keep long-lived logins while still clearing out sessions nobody has come back to.
      • The clock resets every time a session is used, so a session belonging to an active user is never deleted no matter how old it is.
      • Sessions are swept on the interval set by presetOptions.checkPeriod. When a browser turns up with a cookie for a session that has since been deleted, Roosevelt clears that cookie so it does not linger until its own far off expiry.
      • If you prefer or your app depends on sessions pretty much never expiring, set this to 347126472000 (about 11 years) to match the expressSession.cookie.maxAge default.

Default: [Object]

{
  filename: 'sessions.sqlite',
  instance: null,
  preset: 'default',
  presetOptions: {
    checkPeriod: 86400000 // one day
  },
  maxInactivity: 7889238000 // three months
}
  • formidable: Parameters to pass to formidable using formidable's API for multipart form processing (file uploads). Access files uploaded in your controllers by examining the req.files object. Roosevelt will remove any files uploaded to the upload directory when the request ends automatically. To keep any, be sure to move them before the request ends.

Default: [Object]

{
  multiples: true // enables multiple files to be uploaded simultaneously
}

To disable multipart forms entirely, set formidable to false.

  • helmet [Object]: Parameters to pass to the helmet module. This module helps secure Express apps by setting HTTP response headers.

    • The default options are specified in the helmet docs, with the following exceptions that Roosevelt makes to the default Content-Security-Policy settings:
      • The upgrade-insecure-requests directive has been removed. This change prevents this bug.
      • The script-src directive has been set to "unsafe-inline". This makes it possible to use inline scripts.
      • The form-action directive has been set to null. This makes it possible to submit forms to other domains.
      • You can reverse any of these changes by configuring helmet yourself.
    • To disable helmet entirely, set the param to false.
  • logging: Parameters to pass to roosevelt-logger. See roosevelt-logger parameters documentation for configuration options.

Default: [Object]

{
  quieterStartup: false,
  methods: {
    http: true,
    info: true,
    warn: true,
    error: true,
    verbose: false
  }
}
  • logging.quieterStartup [Boolean]: Show notices that repeat on every start at most once a day instead of every time. Default: false.

    • Some notices simply restate how the app is configured, such as which folder is being hosted or that build artifacts are switched off. Nothing is wrong, so seeing them on every restart while developing gets noisy.
    • Only those repeating notices are held back. Anything reporting an actual problem, such as a missing favicon or a file that failed to compile, always prints.
    • Roosevelt remembers which notices it has shown in your system's temp directory, so restarting the app does not bring them all back. Rebooting, or waiting a day, does.
    • Can also be set with the --quieter-startup or -q command line flags, or the QUIETER_STARTUP environment variable.
  • makeBuildArtifacts [Boolean or String]: When enabled Roosevelt will generate user-specified directories, CSS/JS bundles, etc.

    • Defaults to false for apps created manually.
    • Will be set to true in apps generated with the app generator.
    • Can also accept a value of "staticsOnly" which will allow Roosevelt to create static files but skip the creation of the MVC directories.
  • buildOnly [Boolean]: When enabled startServer builds the app and stops there rather than listening for requests. Use it for a build step in CI or a deploy, which has to finish and exit rather than sit on a port. Default: false.

    • Set by the --build and -b command line flags, which also set makeBuildArtifacts to "staticsOnly".
    • Calling initServer (init) instead of startServer has the same effect, and is the better fit when your build script drives Roosevelt directly rather than being handed command line flags.
  • incrementalBuilds [Boolean]: When enabled Roosevelt will skip regenerating a static file if none of the source files it was built from have changed since the last build. This applies to files declared in the copy param too, which are left alone when neither the source nor the copy has changed. Default: true.

    • Set to false to disable the feature and rebuild everything on every start. Deleting your buildFolder has the same one-time effect.
  • routePrefix [String]: A prefix prepended to your application's routes. Applies to all routes and static files. Default: null.

    • Example: When set to "foo" a route bound to / will be instead be bound to /foo/.
    • This prefix is exposed via the routePrefix Express variable which should be used for resolving the absolute paths to statics programmatically.
      • Example: An image located at /images/teddy.jpg can be resolved in a prefix-agnostic way via ${app.get('routePrefix')}/images/teddy.jpg.
  • sitemap [Object]: Serve a sitemap that lists your site's pages for search engines, and a robots.txt that points them to it.

    • enable [Boolean]: Whether to serve the sitemap. Default: false.
    • baseUrl [String]: The address your site is served at, which the paths in the sitemap are added to, since a sitemap has to list full URLs. When it is not set, the address the sitemap was requested at is used. Default: null.
    • path [String]: Where the sitemap is served. Default: "/sitemap.xml".
    • staticPages [Boolean]: List the pages built by the static page generator automatically, at the URLs it writes them to. Default: true.
    • routesFile [String]: The file that says which of your app's routes the sitemap will list. Generated automatically in development mode. Set any route you want exposed to true, and any you want kept out of both the sitemap and search results to false (see noindexExcluded). Review the contents to ensure nothing sensitive is exposed and commit this file to your repo. Default: "sitemap-routes.json".
      • Example: json { "/": true, "/about": true, "/account": false, "/admin/**": false }
      • Keys can be patterns: * matches one part of a path and ** matches any number of them, so "/admin/**" covers everything under /admin, while "/admin/*" would miss /admin/users/new.
    • noindexExcluded [Boolean]: Tell search engines not to list the pages at routes the routes file marks false, such as /account or /admin/**, by sending them with an X-Robots-Tag: noindex header, so that a route left out of the sitemap is also kept out of search results. A route marked false is always one someone decided on, since a route nobody has reviewed yet is null, and gets no header. To leave a route out of the sitemap without keeping it out of search results, set this to false. A controller can also send an X-Robots-Tag header of its own, which replaces this one. Default: true.
    • urls [Function]: A function that returns more URLs to list, for pages your app makes itself that routesFile cannot cover, such as the pages behind a route with parameters, built from a database. Default: null.
      • Example: urls: async app => (await getArticles()).map(article => ({ loc: article.route, lastmod: article.updated }))
      • Each URL can be a path or a full URL, or an object with a loc and a lastmod. Give a lastmod only when you know when the page last changed, since search engines stop trusting one that is always the time the sitemap was made. An object can also have a changefreq and a priority, but Google ignores both, so there is little point.
      • Code that is not in your config file, such as a controller, can add URLs the same way with the sitemap Express variable.
    • exclude [Array of Strings]: Paths to leave out of the sitemap wherever they come from, which may use wildcards, such as ["/drafts/*"]. The routes file already decides for your routes, so this is for static pages and for URLs from urls and add(). Default: [].
    • cacheSeconds [Number]: How long to keep the sitemap before making it again. Call app.get('sitemap').refresh() to make it again sooner, such as after adding a page. Default: null, which is an hour in production mode and every request in development mode.
    • robotsTxt [Boolean or String]: Serve a robots.txt that points to the sitemap. When true, it allows everything. When a path to a file, relative to your app's folder, such as "mvc/views/robots.txt", it is that file, with the line pointing to the sitemap added unless the file has it already. It is read on every request, so edits to it show up straight away. An app with a robots.txt route or public file of its own keeps it, and can add the line pointing to the sitemap with app.get('sitemap').robotsLine(). Default: true.
    • file [Boolean]: Also write the sitemap, and the robots.txt above, into the public folder when the app builds, for a static site whose web server serves its files without Roosevelt. Default: false.
    • A sitemap only helps when the pages it lists agree with it: each should load without a redirect, not ask to be kept out of search results, and not name another page as its canonical URL.
      • A page's canonical URL is the one address search engines should list it at. The same page can often be reached at more than one address, such as with a query string or without one, over http or https, or with a trailing slash or without one, and search engines treat each address as a page of its own unless the page says which one it is. It says so with a <link rel="canonical" href="https://example.com/page"> tag in its <head>, and search engines then list that address, and count links to the others as links to it. A sitemap that lists a different address than the one a page names as canonical is contradicting it, and search engines may ignore the sitemap's URL.
    • The sitemap Express variable has two methods to keep the pages and the sitemap agreeing:
      • app.get('sitemap').canonical(req, loc) makes a page's canonical URL for the <link rel="canonical"> tag.
      • app.get('sitemap').verify(options) can be used to verify the sitemap is well-formed in automated tests. It requests every URL the sitemap lists and resolves to { checked, problems }, where each problem is a { loc, problem } for a URL that does not load, redirects, says not to list it, or names another page as canonical. options.origin is where to request them from instead of the address they are listed at, such as "http://localhost:8080". options.request is a function of your own for making each request, which is given the URL and returns { status, headers, body }. options.rejectUnauthorized can be set to false to accept a certificate made for testing. options.concurrency is how many requests to make at once, which is 10 by default.
  • viewEngine [String]: What templating engine to use, formatted as "fileExtension: nodeModule".

    • Defaults to "none" for apps created manually.
    • Will be set to "html: teddy" in apps generated with the app generator.
    • Also by default when using the app generator, the teddy module is marked as a dependency in package.json.
    • To use multiple templating systems, supply an array of engines to use in the same string format. Each engine you use must also be marked as a dependency in your app's package.json. Whichever engine you supply first with this parameter will be considered the default.

Example configuration using multiple templating systems: [Object]

{
  viewEngine: [
    'html: teddy',
    'php: php',
    'ejs: ejs'
  ]
}