---
title: FileHandler renders files
prefix: FileHandler
icon: filehandler.png
desc: FileHandler renders files
by: TeamGramener
type: microservice
...
[TOC]
[Video](https://youtu.be/4EAArVzmyGo){.youtube}
[gramex.yaml](../gramex.yaml.source) uses the [FileHandler][filehandler]
to display files.
For example, this page is rendered by a Markdown file using the following configuration:
```yaml
url:
markdown:
pattern: /$YAMLURL/(.*) # Any URL under the current gramex.yaml folder
handler: FileHandler # uses this handler
kwargs:
path: $YAMLPATH # Serve files from this YAML file's directory
default_filename: README.md # using README.md as default
index: true # List files if README.md is missing
```
Any file under the current folder is shown as is. If a directory has a
`README.md`, that is shown by default.
`$YAMLURL` is replaced by the current URL's path (in this case, `/filehandler/`)
and `$YAMLPATH` is replaced by the directory of `gramex.yaml`.
**Note**: Gramex comes with a `default` URL handler that automatically serves
files from the home directory of your folder. To prevent that, override the
`default` pattern:
```yaml
url:
default: # This overrides the default URL handler
pattern: ...
```
## Default FileHandler
Even with an empty `gramex.yaml`, Gramex uses a default FileHandler. It does the following:
1. If there's a [`default.template.html` or `default.tmpl.html`](#default-filename), it renders it as a HTML [template](#templates)
2. Else if there's an [`index.html`](#default-filename), it renders as a HTML file (not a template)
3. Else it shows a [directory listing of all files/folders](#directory-listing)
4. It compiles [`.sass` and `.scss`](#sass) files to CSS and renders them
5. It compiles [`.ts`](#typescript) files to JavaScript and renders them
6. It compiles [`.vue`](#vue) single-file components to JavaScript and renders them
7. It compiles `.template.html` and `.tmpl.html` as [templates](#templates) and renders them
8. It [caches on the browser](../cache/#browser-caching) for 60 seconds, but files under
- `assets/` and `favicon.ico` are cached for 1 day
- `node_modules/` are cached for 10 years
[See the default FileHandler configuration](https://github.com/gramener/gramex/blob/master/gramex/gramex.yaml).
To override this, add a FileHandler called `default:` in your `gramex.yaml`. For example:
```yaml
url:
default:
kwargs:
index: false # Disable indices
headers:
Cache-Control: max-age=0 # Disable browser caching
```
## Default filename
When a FileHandler points to a directory:
- If `default_filename:` is specified and exists, it renders that file
- Else, if `index: true`, it [lists files in the directory](#directory-listing)
- Else, it throws an error
You can override `default_filename:` to specify one or more filenames. For example:
```yaml
url:
default-filehandler:
pattern: /$YAMLURL/(.*)
handler: FileHandler
kwargs:
path: $YAMLPATH/directory/
default_filename:
- default.template.html # Serve this as a template, if it exists
- index.html # Else serve this as HTML
- README.md # Else serve this as Markdown to HTML
```
FileHandler checks the files in the order specified in the `default_filename:` The first file that exists is rendered.
## Directory listing
[Video](https://youtu.be/vc6gj1ZFjMo){.youtube}
`index: true` lists all files in the directory if the [`default_filename`](#default-filename) is
missing. To customize the directory listing, specify `index_template: filename`.
This file will be shown as HTML, with `$path` replaced by the directory's
absolute path, and `$body` replaced by a list of all files in that directory.
For example,
```yaml
url:
static:
pattern: /$YAMLURL/static/(.*) # Any URL starting with /static/
handler: FileHandler # uses this handler
kwargs:
path: $YAMLPATH/static/ # Serve files from static/
default_filename: index.html # using index.html as default
index: true # List files if index.html is missing
index_template: $YAMLPATH/template.html # Use template.html to list directory
```
Here is a trivial `template.html`:
```html
$path
$body
```
`index: false` disables directory listing. To disable it for the **default** FileHandler, use:
```yaml
url:
default: # This is a special name for the default Gramex FileHandler
handler: FileHandler
kwargs:
index: false # Disable directory listing here
```
## File Patterns
[Video](https://youtu.be/h7a-TthAsjY){.youtube}
You can map any URL for any file. For example, to map the file
`filehandler/data.csv` to the URL `/filehandler/data`, use this configuration:
```yaml
pattern: /$YAMLURL/filehandler/data # The URL /filehandler/data
handler: FileHandler # uses this handler
kwargs:
path: $YAMLPATH/filehandler/data.csv # and maps to this file
```
You can also map regular expressions to file patterns. For example, to add a
`.yaml` extension automatically to a path, use:
```yaml
url:
yaml-extensions:
pattern: /$YAMLURL/yaml/(.*) # yaml/anything
handler: FileHandler
kwargs:
path: $YAMLPATH/*.yaml # becomes anything.yaml, replacing the * here
```
For example, [yaml/gramex](yaml/gramex) actually renders [gramex.yaml](gramex.yaml.source).
To replace `.html` extension with `.yaml`, use:
```yaml
url:
replace-html-with-yaml:
pattern: /$YAMLURL/(.*)\\.html # Note the double backslash instead of single backslash
handler: FileHandler
kwargs:
path: $YAMLPATH/*.yaml # The part in brackets replaces the * here
```
For more complex mappings, use a dictionary of regular expression mappings:
```yaml
url:
mapping:
pattern: /$YAMLURL/node/((foo|bar)/.*) # match /node/foo/... and node//bar/...
handler: FileHandler
kwargs:
path: # If path: is a dict, it's treated as a mapping
"foo/": $YAMLPATH/foo.html # /node/foo/ -> foo.html
"bar/": $YAMLPATH/bar.html # /node/bar/ -> bar.html
"foo/(.*)": $YAMLPATH/foo/{0}.html # /node/foo/x -> foo/x.html
"bar/(?P.*)": $YAMLPATH/bar/{file}.html # /node/bar/x -> bar/x.html
".*": $YAMLPATH/default.html # anything else -> default.html
```
The mapping has keys that are regular expressions. They must match the part of
the URL in brackets. (If there are multiple brackets, it matches the first one.)
Values are file paths. They are formatted as string templates using the regular
expression match groups and URL query parameters. So:
- `{0}` matches the first capture group (in brackets, like `(.*)`),
`{1}` matches the second capture group, etc.
- `{file}` matches the named capture group `(?P.*)`, etc
- If there's a URL query parameter `?file=`, the first value that replaces
`{file}` -- but only if there is no such named capture group
When using URL query parameters, you should provide default values in case the
request does not pass the parameter. You can do this using `default:`
```yaml
url:
mapping:
pattern: /$YAMLURL/ # Home page
handler: FileHandler
kwargs:
path: # If path: is a dict, it's treated as a mapping
"": $YAMLPATH/{dir}/{file}.{ext} # /?dir=foo&file=bar&ext=txt -> foo/bar.txt
default:
dir: "" # ?dir= is the default
file: index # ?file=index is the default
ext: html # ?ext=html is the default
```
If you want to map a subset of files to a folder, you can mark them in the
pattern. For example, this configuration maps `/style.css` and `/script.js` to
the home directory. To ensure that this takes priority over others, you can add
a higher value to the `priority` (which defaults to 0.)
```yaml
url:
assets:
pattern: /(style.css|script.js) # Any of these to URLs
priority: 2 # Give it a higher priority
handler: FileHandler # uses this handler
kwargs:
path: . # Serve files from /
```
This can work across directories as well. For example, this maps the `static`
and `bower_components` and specifies a 1-day expiry for any files under them.
```yaml
url:
static-files:
# Any file under the current directory, starting with bower_components/
# or with static/ is mapped to a FileHandler
pattern: /$YAMLURL/(bower_components/.*|static/.*)
handler: FileHandler
kwargs:
path: $YAMLPATH/ # Base is the current directory
headers:
Cache-Control: public, max-age=86400 # Cache publicly for 1 day
```
## Caching
See how to cache [static files](../cache/#static-files)
## Redirecting files
See [File patterns](#file-patterns)
## Ignore files
To prevent certain files from ever being served, specify the
`handlers.FileHandler.ignore` setting. By default, this is:
```yaml
handlers:
FileHandler:
ignore:
- gramex.yaml # Always ignore gramex.yaml in Filehandlers
- ".*" # Hide dotfiles
```
The `gramex.yaml` file and all files beginning with `.` will be hidden by
default. You can change the above setting in your `gramex.yaml` file. For example:
```yaml
handlers:
FileHandler:
ignore:
- ".*" # Protect dot-files - they are usually meant to be hidden
- "*.git*" # Protect .gitignore, .gitattributes, etc - they list filenames
- "*.git/*" # Protect all files under the .git/ repo - they have code history
- "*.yaml" # Protect YAML files - they list all URLs
```
You can customize this further for each handler via the `allow:` and `ignore:` configurations.
For example:
```yaml
url:
my-app-files:
pattern: /$YAMLURL/(.*)
handler: FileHandler
kwargs:
path: .
ignore:
- "*.xls*" # Ignore all Excel files
allow:
- public.xlsx # But allow public.xlsx
```
Now `public.xlsx` is accessible. But `something-else.xlsx` will raise a HTTP 403 error.
The log reports `Disallow: "something-else.xlsx". It matches "*.xls*"`.
If you import [deploy.yaml](../deploy/#security), FileHandler blocks all files
except specific white-listed exceptions.
## MIME types
[Video](https://youtu.be/wPDo7CEECs4){.youtube}
The URL will be served with the MIME type of the file. CSV files have a MIME
type `text/csv` and a `Content-Disposition` set to download the file. You
can override these headers:
```yaml
pattern: /filehandler/data
handler: FileHandler
kwargs:
path: filehandler/data.csv
headers:
Content-Type: text/plain # Display as plain text
Content-Disposition: none # Do not download the file
```
To convert a file type into an attachment, use:
```yaml
pattern: /filehandler/data
handler: FileHandler
kwargs:
path: filehandler/data.txt
headers:
Content-Type: text/plain
Content-Disposition: attachment; filename=data.txt # Save as data.txt
```
From **v1.23.1**, to serve different files with different MIME types, use file patterns:
```yaml
pattern: /$YAMLURL/(.*)
handler: FileHandler
kwargs:
path: $YAMLPATH
headers:
Content-Type: text/plain # Default header
"*.json": # Override headers on .json files
Content-Type: application/json
"json/**" # Override headers on json/ directory
Content-Type: application/json
```
## Templates
[Video](https://youtu.be/0kzphobMQnU){.youtube}
FileHandler uses [Tornado templates][template] to generate content from data.
For example, this `page.tmpl.html` renders 10 images:
```text
{% for id in range(1, 10) %}
{% end %}
```
The default, any file that ends with `.tmpl.html` or `.template.html` is rendered as a template.
You can specify amy file patterns using `template: patterns`. For example:
```yaml
url:
template:
pattern: ...
handler: FileHandler
kwargs:
path: ...
template: ["template*.html", "*.tmpl.html", "*.svg"]
```
::: example href=template source="https://github.com/gramener/gramex-guide/blob/master/filehandler/template.html"
Template example
### Template syntax
Templates can use all variables in the [template syntax][template-syntax]. This includes:
- `handler`: the current request handler object
- all [Tornado handler attributes](../handlers/#tornado-handler-attributes), like
`handler.request.uri`, `handler.current_user`, etc.
- ... and [BaseHandler attributes](../handlers/#basehandler-attributes), like
`handler.args` - a dict of lists containing the URL query parameters
- `request`: alias for `handler.request`
- `current_user`: alias for `handler.current_user`
### Sub-templates
Templates import sub-templates using `{% include path/to/template.html %}`.
- The path is **relative** to the parent template.
- **Variables** from the template are available to the sub-template.
For example:
```text
{% set title, menu = 'App name', ['Home', 'Dashboard'] %}
{% include path/relative/to/template/navbar.html %}
```
### Dynamic sub-templates
Use `{{ gramex.cache.open(file) }}` if `file` is a variable. For example:
```text
Insert {user}.txt:
{% set user = handler.current_user.id %}
{% raw gramex.cache.open(f'{user}.txt', rel=True) %}
Insert {user}.md after converting to HTML:
{% raw gramex.cache.open(f'{user}.md', rel=True) %}
Render {user}.html as a Tornado template, passing a `user` variable:
{% raw gramex.cache.open(f'{user}.html', rel=True).generate(user=handler.current_user) %}
```
See [gramex.cache.open()](../api/cache/#gramex.cache.open) for more formats options.
`rel=True` loads the file relative to the **template** path.
### UI Modules
Templates import [modules](https://www.tornadoweb.org/en/stable/guide/templates.html#ui-modules)
using `{% module Template('path/to/template.html', **kwargs) %}`.
For example:
```text
Import navbar.html in-place as a template.
{% module Template('path/relative/to/filehandler/navbar.html',
title='App name',
menu=['Home', 'Dashboard'])
%}
```
Note:
- The path is relative to the FileHandler root path (which may be different from the parent template location)
- Parent template variables are NOT available in the sub-template. Only the passed kwargs are available.
### Redirecting templates
To redirect to a page (e.g. `new_url`) from a template, add this code:
```html
{% set handler.redirect(new_url) %} {% set handler.include_body = False %} {%
set return b'' %}
```
1. `handler.redirect(new_url)` redirects to `new_url`
2. `handler.include_body = False` prevents FileHandler from rendering any returned text
3. `return b''` returns immediately with an empty bytestring -- avoiding rendering the rest of the template
Or you can create a `utils.py` that has a `redirect_template() like this:
```python
import tornado.gen
def redirect_template(handler, new_url):
handler.redirect(new_url)
handler.include_body = False
raise tornado.gen.Return(b'')
```
... and call it in your template:
```html
{% import utils %} {% set utils.redirect_template(handler, new_url) %}
```
## SASS
[Video](https://youtu.be/ryYjE5R9yX4){.youtube}
FileHandler can compile [SCSS files](http://sass-lang.com/). The default FileHandler compiles any
`.scss` or `.sass` file is compiled into CSS. For example, this `color.scss` file:
```scss
$color: red !default;
body {
background-color: lighten($color, 40%);
}
```
... [is rendered as](color.scss):
```css
body {
background-color: #fcc;
}
```
::: example href=color.scss source="https://github.com/gramener/gramex-guide/blob/master/filehandler/color.scss"
See color.scss
To enable this in your FileHandler, add:
```yaml
kwargs:
...
sass: '*.scss, *.sass' # Compile SCSS and SASS files into CSS
```
URL query parameters are automatically passed as variables to the SASS file. For example,
`color.scss?color=green` sets `$color: green`.
::: example href=color.scss?color=blue source="https://github.com/gramener/gramex-guide/blob/master/filehandler/color.scss"
See `color.scss?color=blue`
You can use this to allow users to customize your theme.
You can also pass a `?@import=path/to/filename.sass` to include a file in the SASS file. This SASS
file path must be relative to the requested SASS file or the directory Gramex is running in.
## TypeScript
FileHandler can compile [TypeScript](https://www.typescriptlang.org/).
The default FileHandler compiles any `.ts` file into JS. For example, this `typescript.ts` file:
```ts
type WindowStates = "open" | "closed" | "minimized";
function getLength(obj: WindowStates | WindowStates[]) {
return obj.length;
}
```
... [is rendered as](typescript.ts):
```js
function getLength(obj) {
return obj.length;
}
//# sourceMappingURL=typescript.ts?map
```
::: example href=typescript.ts source="https://github.com/gramener/gramex-guide/blob/master/filehandler/typescript.ts"
See typescript.ts
To enable this in your FileHandler, add:
```yaml
kwargs:
...
ts: '*.ts' # Compile .ts files into JS
```
Gramex uses [esbuild](https://esbuild.github.io/api/) to compile TypeScript. The following options are supported:
- [`?format=`](https://esbuild.github.io/api/#format): `iife` (default) or `esm`
- `
```
... [is rendered as](hello-world.vue):
```js
(function(t){var e={};function n(r){if(e[r]) /* JS contents trimmed */
```
::: example href=hello-world.vue source="https://github.com/gramener/gramex-guide/blob/master/filehandler/hello-world.vue"
See hello-world.vue
To enable this in your FileHandler, add:
```yaml
kwargs:
...
vue: '*.vue' # Compile .vue files into JS
```
## XSRF
[Video](https://youtu.be/2_rogB8UzXQ){.youtube}
If you're submitting forms using the POST method, you need to submit an
[\_xsrf][xsrf] field that has the value of the `_xsrf` cookie.
**When using HTML forms**, you can include it in the template using handlers'
built-in `xsrf_token` property:
```html
```
To render a file as a template, use:
```yaml
url:
template:
pattern: /page # The URL /page
handler: FileHandler # displays a file
kwargs:
path: page.html # named page.html
template: true # Render as a template
```
[xsrf]: http://www.tornadoweb.org/en/stable/guide/security.html#cross-site-request-forgery-protection
**When using AJAX**, **v1.85** onwards, no XSRF token is required, because Gramex checks for this automatically.
1. XMLHttpRequest (e.g. via jQuery.post, jQuery.ajax, etc) automatically sends an `X-Requested-With: XMLHttpRequest` header for AJAX.
2. [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) automatically adds
[Sec-Fetch-Mode: cors](https://developer.mozilla.org/en-US/docs/Glossary/Fetch_metadata_request_header) headers
**When submitting from a server**, add either of these headers to bypass the check, like this:
```python
import requests
requests.post('http://example.org/page', headers={'X-Requested-With': 'XMLHttpRequest'})
requests.post('http://example.org/page', headers={'Sec-Fetch-Mode': 'cors'})
```
You can disable XSRF for a specific handler like this:
```yaml
url:
name:
pattern: ... # When this page is visited,
handler: ... # no matter what the handler is,
kwargs:
xsrf_cookies: false # Disable XSRF cookies
```
You can disable XSRF for _all handlers_ like this (but this is **not recommended**):
```yaml
app:
settings:
xsrf_cookies: false
```
For debugging without XSRF, start Gramex with a `--settings.xsrf_cookies=false` from the command line.
The XSRF cookie is automatically set when a FileHandler [template](#templates)
accesses `handler.xsrf_token`. You can also set it explicitly, by adding a
`set_xsrf: true` configuration to `kwargs` like this:
```yaml
url:
name:
pattern: ... # When this page is visited,
handler: ... # no matter what the handler is,
kwargs:
...
set_xsrf: true # set the xsrf cookie
```
How XSRF works:
- Tornado sets a random non-expiring `_xsrf` cookie when
`tornado.web.RequestHandler.xsrf_token()` is called. It is `httponly` by
default in Gramex because of the default `xsrf_cookie_kwargs` setting, but not
set to `secure` to allow HTTP sites to access it
- Checks if the `_xsrf` GET/POST argument (or the `X-Xsrftoken` or `C-Xsrftoken`
headers) is a valid XSRF token, and checks that it matches the cookie value
## FileHandler HTTP methods
[Video](https://youtu.be/S86H9FxClYY){.youtube}
By default FileHandler supports `GET`, `HEAD` and `POST` methods. You can map
any of the following methods to the file using the `methods:` configuration as
follows:
```yaml
url:
name:
pattern: ...
handler: FileHandler
kwargs:
...
methods: [GET, HEAD, POST, DELETE, PATCH, PUT, OPTIONS]
```
## File concatenation
[Video](https://youtu.be/opBiI7LJNns){.youtube}
You can concatenate multiple files and serve them as a single file. For example:
```yaml
...
pattern: /libraries.js
handler: FileHandler
kwargs:
path:
- bower_components/jquery/dist/jquery.min.js
- bower_components/bootstrap/dist/bootstrap.min.js
- bower_components/d3/d3.v3.min.js
headers:
Cache-Control: public, max-age=86400 # Cache publicly for 1 day
```
This concatenates all files in `path` in sequence. If transforms are
specified, the transforms are applied before concatenation.
This is useful to pack multiple static files into one, as the example shows.
[filehandler]: https://gramener.com/gramex/guide/api/handlers/#gramex.handlers.FileHandler
[template]: http://www.tornadoweb.org/en/stable/template.html
[template-syntax]: http://www.tornadoweb.org/en/stable/guide/templates.html#template-syntax
## Transforming content
Use `transform:` to apply functions before rendering the content. [Templates](#templates),
[SASS](#sass), [Vue](#vue) and [TypeScript](#typescript) use this feature behind the scenes.
Any function can be used to transform. For example, this renders `.md` or `.markdown` files as HTML
using [python-markdown](https://python-markdown.github.io/reference/):
```yaml
# ... contd ...
transform:
"*.md, *.markdown": # Any file matching .md or .markdown
function: markdown.markdown(content, output_format='html5', extensions=['extra'])
encoding: utf-8 # Read input file as UTF-8
headers: # Use these HTTP headers:
Content-Type: text/html # MIME type: text/html
```
Transform keys matches one or more [glob patterns](https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob)
separated by space/comma (e.g. `'*.md, 'data/**'`.)
Transform values are dicts that accepts these keys:
- **function**: The expression to return. Example: `function: mymodule.transform(content, handler)`.
- `content` has the file contents
- `handler` has the FileHandler object
- **encoding**: The encoding to read the file with, e.g. `utf-8`.
If `None` (the default), the file is read as bytes, and the transform `function` MUST accept the content as bytes
- **headers**: HTTP headers for the response
## Configure all FileHandlers
Gramex adds the following kwargs to **all** FileHandlers for security reasons, [ignoring these files](#ignore-files):
1. Any `gramex.yaml` files (`gramex*.yaml`)
2. Any files beginning with `.` (`.*`)
3. Any Python files (`.py*`)
To modify this, update `handlers.FileHandler` in your `gramex.yaml`. For example:
```yaml
handlers:
FileHandler:
ignore:
- gramex*.yaml # Always ignore gramex config files
- ".*" # Hide dotfiles
- "*.py*" # Hide Python scripts
- "*secret*" # Ignore all secret files
allow:
- "README.*" # Always allow README files, even README.secret
```
[See the default FileHandler configuration](https://github.com/gramener/gramex/blob/master/gramex/gramex.yaml).