caddy

package module
v0.9.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 28, 2016 License: Apache-2.0 Imports: 20 Imported by: 0

README

Caddy

community twitter Documentation Linux Build Status Windows Build Status

Caddy is a general-purpose web server for Windows, Mac, Linux, BSD, and Android. It is a capable but easier alternative to other popular web servers.

Releases · User Guide · Community

Menu

Features

  • Easy configuration with Caddyfile
  • Automatic HTTPS via Let's Encrypt; Caddy obtains and manages all cryptographic assets for you
  • HTTP/2 enabled by default (powered by Go standard library)
  • Virtual hosting for hundreds of sites per server instance, including TLS SNI
  • Experimental QUIC support for those that like speed
  • TLS session ticket key rotation for more secure connections
  • Brilliant extensibility so Caddy can be customized for your needs
  • Runs anywhere with no external dependencies (not even libc)

Quick Start

Caddy binaries have no dependencies and are available for every platform. Install Caddy any one of these ways:

Once caddy is in your PATH, you can cd to your website's folder and run caddy to serve it. By default, Caddy serves the current directory at localhost:2015.

To customize how your site is served, create a file named Caddyfile by your site and paste this into it:

localhost

gzip
browse
websocket /echo cat
ext    .html
log    /var/log/access.log
proxy  /api 127.0.0.1:7005
header /api Access-Control-Allow-Origin *

When you run caddy in that directory, it will automatically find and use that Caddyfile to configure itself.

This simple file enables compression, allows directory browsing (for folders without an index file), hosts a WebSocket echo server at /echo, serves clean URLs, logs requests to access.log, proxies all API requests to a backend on port 7005, and adds the coveted Access-Control-Allow-Origin: * header for all responses from the API.

Wow! Caddy can do a lot with just a few lines.

To host multiple sites and do more with the Caddyfile, please see the Caddyfile documentation.

Note that production sites are served over HTTPS by default.

Caddy has a command line interface. Run caddy -h to view basic help or see the CLI documentation for details.

Running as root: We advise against this. You can still listen on ports < 1024 using setcap like so: sudo setcap cap_net_bind_service=+ep ./caddy

Running from Source

Note: You will need Go 1.6 or newer.

  1. go get github.com/mholt/caddy/caddy
  2. cd into your website's directory
  3. Run caddy (assuming $GOPATH/bin is in your $PATH)

Caddy's main() is in the caddy subfolder. To recompile Caddy, use build.bash found in that folder.

Running in Production

The Caddy project does not officially maintain any system-specific integrations, but your download file includes unofficial resources contributed by the community that you may find helpful for running Caddy in production.

How you choose to run Caddy is up to you. Many users are satisfied with nohup caddy &. Others use screen. Users who need Caddy to come back up after reboots either do so in the script that caused the reboot, add a command to an init script, or configure a service with their OS.

Contributing

Join our community where you can chat with other Caddy users and developers!

Please see our contributing guidelines and check out the developer wiki.

We use GitHub issues and pull requests only for discussing bug reports and the development of specific changes. We welcome all other topics on the forum!

If you want to contribute to the documentation, please submit pull requests to caddyserver/caddyserver.com.

Thanks for making Caddy -- and the Web -- better!

Special thanks to DigitalOcean for hosting the Caddy project.

About the Project

Caddy was born out of the need for a "batteries-included" web server that runs anywhere and doesn't have to take its configuration with it. Caddy took inspiration from spark, nginx, lighttpd, Websocketd and Vagrant, which provides a pleasant mixture of features from each of them.

The name "Caddy": The name of the software is "Caddy", not "Caddy Server" or "CaddyServer". Please call it "Caddy" or, if you wish to clarify, "the Caddy web server". See brand guidelines.

Author on Twitter: @mholt6

Documentation

Overview

Package caddy implements the Caddy server manager.

To use this package:

  1. Set the AppName and AppVersion variables.
  2. Call LoadCaddyfile() to get the Caddyfile. Pass in the name of the server type (like "http").
  3. Call caddy.Start() to start Caddy. You get back an Instance, on which you can call Restart() to restart it or Stop() to stop it.

You should call Wait() on your instance to wait for all servers to quit before your process exits.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// AppName is the name of the application.
	AppName string

	// AppVersion is the version of the application.
	AppVersion string

	// Quiet mode will not show any informative output on initialization.
	Quiet bool

	// PidFile is the path to the pidfile to create.
	PidFile string

	// GracefulTimeout is the maximum duration of a graceful shutdown.
	GracefulTimeout time.Duration
)

Configurable application parameters

View Source
var (
	// DefaultConfigFile is the name of the configuration file that is loaded
	// by default if no other file is specified.
	DefaultConfigFile = "Caddyfile"
)

Functions

func AssetsPath

func AssetsPath() string

AssetsPath returns the path to the folder where the application may store data. If CADDYPATH env variable is set, that value is used. Otherwise, the path is the result of evaluating "$HOME/.caddy".

func DescribePlugins

func DescribePlugins() string

DescribePlugins returns a string describing the registered plugins.

func HasListenerWithAddress

func HasListenerWithAddress(addr string) bool

HasListenerWithAddress returns whether this package is tracking a server using a listener with the address addr.

func IsLoopback

func IsLoopback(addr string) bool

IsLoopback returns true if the hostname of addr looks explicitly like a common local hostname. addr must only be a host or a host:port combination.

func IsUpgrade

func IsUpgrade() bool

IsUpgrade returns true if this process is part of an upgrade where a parent caddy process spawned this one to ugprade the binary.

func RegisterCaddyfileLoader

func RegisterCaddyfileLoader(name string, loader Loader)

RegisterCaddyfileLoader registers loader named name.

func RegisterParsingCallback

func RegisterParsingCallback(serverType, afterDir string, callback ParsingCallback)

RegisterParsingCallback registers callback to be called after executing the directive afterDir for server type serverType.

func RegisterPlugin

func RegisterPlugin(name string, plugin Plugin)

RegisterPlugin plugs in plugin. All plugins should register themselves, even if they do not perform an action associated with a directive. It is important for the process to know which plugins are available.

The plugin MUST have a name: lower case and one word. If this plugin has an action, it must be the name of the directive that invokes it. A name is always required and must be unique for the server type.

func RegisterServerType

func RegisterServerType(typeName string, srv ServerType)

RegisterServerType registers a server type srv by its name, typeName.

func SetDefaultCaddyfileLoader

func SetDefaultCaddyfileLoader(name string, loader Loader)

SetDefaultCaddyfileLoader registers loader by name as the default Caddyfile loader if no others produce a Caddyfile. If another Caddyfile loader has already been set as the default, this replaces it.

Do not call RegisterCaddyfileLoader on the same loader; that would be redundant.

func SplitCommandAndArgs

func SplitCommandAndArgs(command string) (cmd string, args []string, err error)

SplitCommandAndArgs takes a command string and parses it shell-style into the command and its separate arguments.

Example
var commandLine string
var command string
var args []string

// just for the test - change GOOS and reset it at the end of the test
runtimeGoos = "windows"
defer func() {
	runtimeGoos = runtime.GOOS
}()

commandLine = `mkdir /P "C:\Program Files"`
command, args, _ = SplitCommandAndArgs(commandLine)

fmt.Printf("Windows: %s: %s [%s]\n", commandLine, command, strings.Join(args, ","))

// set GOOS to linux
runtimeGoos = "linux"

commandLine = `mkdir -p /path/with\ space`
command, args, _ = SplitCommandAndArgs(commandLine)

fmt.Printf("Linux: %s: %s [%s]\n", commandLine, command, strings.Join(args, ","))
Output:

Windows: mkdir /P "C:\Program Files": mkdir [/P,C:\Program Files]
Linux: mkdir -p /path/with\ space: mkdir [-p,/path/with space]

func Started added in v0.9.1

func Started() bool

Started returns true if at least one instance has been started by this package. It never gets reset to false once it is set to true.

func Stop

func Stop() error

Stop stops ALL servers. It blocks until they are all stopped. It does NOT execute shutdown callbacks, and it deletes all instances after stopping is completed. Do not re-use any references to old instances after calling Stop.

func TrapSignals

func TrapSignals()

TrapSignals create signal handlers for all applicable signals for this system. If your Go program uses signals, this is a rather invasive function; best to implement them yourself in that case. Signals are not required for the caddy package to function properly, but this is a convenient way to allow the user to control this part of your program.

func Upgrade

func Upgrade() error

Upgrade re-launches the process, preserving the listeners for a graceful restart. It does NOT load new configuration; it only starts the process anew with a fresh binary.

TODO: This is not yet implemented

func ValidDirectives

func ValidDirectives(serverType string) []string

ValidDirectives returns the list of all directives that are recognized for the server type serverType. However, not all directives may be installed. This makes it possible to give more helpful error messages, like "did you mean ..." or "maybe you need to plug in ...".

Types

type AfterStartup

type AfterStartup interface {
	OnStartupComplete()
}

AfterStartup is an interface that can be implemented by a server type that wants to run some code after all servers for the same Instance have started.

type CaddyfileInput

type CaddyfileInput struct {
	Filepath       string
	Contents       []byte
	ServerTypeName string
}

CaddyfileInput represents a Caddyfile as input and is simply a convenient way to implement the Input interface.

func (CaddyfileInput) Body

func (c CaddyfileInput) Body() []byte

Body returns c.Contents.

func (CaddyfileInput) Path

func (c CaddyfileInput) Path() string

Path returns c.Filepath.

func (CaddyfileInput) ServerType

func (c CaddyfileInput) ServerType() string

ServerType returns c.ServerType.

type Context

type Context interface {
	// Called after the Caddyfile is parsed into server
	// blocks but before the directives are executed,
	// this method gives you an opportunity to inspect
	// the server blocks and prepare for the execution
	// of directives. Return the server blocks (which
	// you may modify, if desired) and an error, if any.
	// The first argument is the name or path to the
	// configuration file (Caddyfile).
	//
	// This function can be a no-op and simply return its
	// input if there is nothing to do here.
	InspectServerBlocks(string, []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error)

	// This is what Caddy calls to make server instances.
	// By this time, all directives have been executed and,
	// presumably, the context has enough state to produce
	// server instances for Caddy to start.
	MakeServers() ([]Server, error)
}

Context is a type which carries a server type through the load and setup phase; it maintains the state between loading the Caddyfile, then executing its directives, then making the servers for Caddy to manage. Typically, such state involves configuration structs, etc.

type Controller

type Controller struct {
	caddyfile.Dispenser

	// Key is the key from the top of the server block, usually
	// an address, hostname, or identifier of some sort.
	Key string

	// OncePerServerBlock is a function that executes f
	// exactly once per server block, no matter how many
	// hosts are associated with it. If it is the first
	// time, the function f is executed immediately
	// (not deferred) and may return an error which is
	// returned by OncePerServerBlock.
	OncePerServerBlock func(f func() error) error

	// ServerBlockIndex is the 0-based index of the
	// server block as it appeared in the input.
	ServerBlockIndex int

	// ServerBlockKeyIndex is the 0-based index of this
	// key as it appeared in the input at the head of the
	// server block.
	ServerBlockKeyIndex int

	// ServerBlockKeys is a list of keys that are
	// associated with this server block. All these
	// keys, consequently, share the same tokens.
	ServerBlockKeys []string

	// ServerBlockStorage is used by a directive's
	// setup function to persist state between all
	// the keys on a server block.
	ServerBlockStorage interface{}
	// contains filtered or unexported fields
}

Controller is given to the setup function of directives which gives them access to be able to read tokens with which to configure themselves. It also stores state for the setup functions, can get the current context, and can be used to identify a particular server block using the Key field.

func NewTestController

func NewTestController(serverType, input string) *Controller

NewTestController creates a new Controller for the server type and input specified. The filename is "Testfile". If the server type is not empty and is plugged in, a context will be created so that the results of setup functions can be checked for correctness.

Used only for testing, but exported so plugins can use this for convenience.

func (*Controller) Context

func (c *Controller) Context() Context

Context gets the context associated with the instance associated with c.

func (*Controller) OnFinalShutdown

func (c *Controller) OnFinalShutdown(fn func() error)

OnFinalShutdown adds fn to the list of callback functions to execute when the server is about to be shut down NOT as part of a restart.

func (*Controller) OnFirstStartup

func (c *Controller) OnFirstStartup(fn func() error)

OnFirstStartup adds fn to the list of callback functions to execute when the server is about to be started NOT as part of a restart.

func (*Controller) OnRestart

func (c *Controller) OnRestart(fn func() error)

OnRestart adds fn to the list of callback functions to execute when the server is about to be restarted.

func (*Controller) OnShutdown

func (c *Controller) OnShutdown(fn func() error)

OnShutdown adds fn to the list of callback functions to execute when the server is about to be shut down (including restarts).

func (*Controller) OnStartup

func (c *Controller) OnStartup(fn func() error)

OnStartup adds fn to the list of callback functions to execute when the server is about to be started (including restarts).

func (*Controller) ServerType

func (c *Controller) ServerType() string

ServerType gets the name of the server type that is being set up.

type GracefulServer

type GracefulServer interface {
	Server
	Stopper

	// Address returns the address the server should
	// listen on; it is used to pair the server to
	// its listener during a graceful/zero-downtime
	// restart. Thus when implementing this method,
	// you must not access a listener to get the
	// address; you must store the address the
	// server is to serve on some other way.
	Address() string
}

GracefulServer is a Server and Stopper, the stopping of which is graceful (whatever that means for the kind of server being implemented). It must be able to return the address it is configured to listen on so that its listener can be paired with it upon graceful restarts. The net.Listener that a GracefulServer creates must implement the Listener interface for restarts to be graceful (assuming the listener is for TCP).

type Input

type Input interface {
	// Gets the Caddyfile contents
	Body() []byte

	// Gets the path to the origin file
	Path() string

	// The type of server this input is intended for
	ServerType() string
}

Input represents a Caddyfile; its contents and file path (which should include the file name at the end of the path). If path does not apply (e.g. piped input) you may use any understandable value. The path is mainly used for logging, error messages, and debugging.

func CaddyfileFromPipe

func CaddyfileFromPipe(f *os.File, serverType string) (Input, error)

CaddyfileFromPipe loads the Caddyfile input from f if f is not interactive input. f is assumed to be a pipe or stream, such as os.Stdin. If f is not a pipe, no error is returned but the Input value will be nil. An error is only returned if there was an error reading the pipe, even if the length of what was read is 0.

func DefaultInput

func DefaultInput(serverType string) Input

DefaultInput returns the default Caddyfile input to use when it is otherwise empty or missing. It uses the default host and port (depends on host, e.g. localhost is 2015, otherwise 443) and root.

func LoadCaddyfile

func LoadCaddyfile(serverType string) (Input, error)

LoadCaddyfile loads a Caddyfile by calling the plugged in Caddyfile loader methods. An error is returned if more than one loader returns a non-nil Caddyfile input. If no loaders load a Caddyfile, the default loader is used. If no default loader is registered or it returns nil, the server type's default Caddyfile is loaded. If the server type does not specify any default Caddyfile value, then an empty Caddyfile is returned. Consequently, this function never returns a nil value as long as there are no errors.

type Instance

type Instance struct {
	// contains filtered or unexported fields
}

Instance contains the state of servers created as a result of calling Start and can be used to access or control those servers.

func Start

func Start(cdyfile Input) (*Instance, error)

Start starts Caddy with the given Caddyfile.

This function blocks until all the servers are listening.

func (*Instance) Caddyfile

func (i *Instance) Caddyfile() Input

Caddyfile returns the Caddyfile used to create i.

func (*Instance) Restart

func (i *Instance) Restart(newCaddyfile Input) (*Instance, error)

Restart replaces the servers in i with new servers created from executing the newCaddyfile. Upon success, it returns the new instance to replace i. Upon failure, i will not be replaced.

func (*Instance) SaveServer

func (i *Instance) SaveServer(s Server, ln net.Listener)

SaveServer adds s and its associated listener ln to the internally-kept list of servers that is running. For saved servers, graceful restarts will be provided.

func (*Instance) Servers added in v0.9.2

func (i *Instance) Servers() []ServerListener

Servers returns the ServerListeners in i.

func (*Instance) ShutdownCallbacks

func (i *Instance) ShutdownCallbacks() []error

ShutdownCallbacks executes all the shutdown callbacks of i, including ones that are scheduled only for the final shutdown of i. An error returned from one does not stop execution of the rest. All the non-nil errors will be returned.

func (*Instance) Stop

func (i *Instance) Stop() error

Stop stops all servers contained in i. It does NOT execute shutdown callbacks.

func (*Instance) Wait

func (i *Instance) Wait()

Wait blocks until all of i's servers have stopped.

type Listener

type Listener interface {
	net.Listener
	File() (*os.File, error)
}

Listener is a net.Listener with an underlying file descriptor. A server's listener should implement this interface if it is to support zero-downtime reloads.

type Loader

type Loader interface {
	Load(serverType string) (Input, error)
}

Loader is a type that can load a Caddyfile. It is passed the name of the server type. It returns an error only if something went wrong, not simply if there is no Caddyfile for this loader to load.

A Loader should only load the Caddyfile if a certain condition or requirement is met, as returning a non-nil Input value along with another Loader will result in an error. In other words, loading the Caddyfile must be deliberate & deterministic, not haphazard.

The exception is the default Caddyfile loader, which will be called only if no other Caddyfile loaders return a non-nil Input. The default loader may always return an Input value.

type LoaderFunc

type LoaderFunc func(serverType string) (Input, error)

LoaderFunc is a convenience type similar to http.HandlerFunc that allows you to use a plain function as a Load() method.

func (LoaderFunc) Load

func (lf LoaderFunc) Load(serverType string) (Input, error)

Load loads a Caddyfile.

type PacketConn

type PacketConn interface {
	net.PacketConn
	File() (*os.File, error)
}

PacketConn is a net.PacketConn with an underlying file descriptor. A server's packetconn should implement this interface if it is to support zero-downtime reloads (in sofar this holds true for datagram connections).

type ParsingCallback

type ParsingCallback func(Context) error

ParsingCallback is a function that is called after a directive's setup functions have been executed for all the server blocks.

type Plugin

type Plugin struct {
	// ServerType is the type of server this plugin is for.
	// Can be empty if not applicable, or if the plugin
	// can associate with any server type.
	ServerType string

	// Action is the plugin's setup function, if associated
	// with a directive in the Caddyfile.
	Action SetupFunc
}

Plugin is a type which holds information about a plugin.

type Server

type Server interface {
	TCPServer
	UDPServer
}

Server is a type that can listen and serve. It supports both TCP and UDP, although the UDPServer interface can be used for more than just UDP.

If the server uses TCP, it should implement TCPServer completely. If it uses UDP or some other protocol, it should implement UDPServer completely. If it uses both, both interfaces should be fully implemented. Any unimplemented methods should be made as no-ops that simply return nil values.

type ServerListener added in v0.9.2

type ServerListener struct {
	// contains filtered or unexported fields
}

ServerListener pairs a server to its listener and/or packetconn.

func (ServerListener) Addr added in v0.9.2

func (s ServerListener) Addr() net.Addr

Addr returns the listener's network address. It returns nil when it is not set.

func (ServerListener) LocalAddr added in v0.9.2

func (s ServerListener) LocalAddr() net.Addr

LocalAddr returns the local network address of the packetconn. It returns nil when it is not set.

type ServerType

type ServerType struct {
	// Function that returns the list of directives, in
	// execution order, that are valid for this server
	// type. Directives should be one word if possible
	// and lower-cased.
	Directives func() []string

	// DefaultInput returns a default config input if none
	// is otherwise loaded. This is optional, but highly
	// recommended, otherwise a blank Caddyfile will be
	// used.
	DefaultInput func() Input

	// The function that produces a new server type context.
	// This will be called when a new Caddyfile is being
	// loaded, parsed, and executed independently of any
	// startup phases before this one. It's a way to keep
	// each set of server instances separate and to reduce
	// the amount of global state you need.
	NewContext func() Context
}

ServerType contains information about a server type.

type SetupFunc

type SetupFunc func(c *Controller) error

SetupFunc is used to set up a plugin, or in other words, execute a directive. It will be called once per key for each server block it appears in.

func DirectiveAction

func DirectiveAction(serverType, dir string) (SetupFunc, error)

DirectiveAction gets the action for directive dir of server type serverType.

type Stopper

type Stopper interface {
	// Stop stops the server. It blocks until the
	// server is completely stopped.
	Stop() error
}

Stopper is a type that can stop serving. The stop does not necessarily have to be graceful.

type TCPServer

type TCPServer interface {
	// Listen starts listening by creating a new listener
	// and returning it. It does not start accepting
	// connections. For UDP-only servers, this method
	// can be a no-op that returns (nil, nil).
	Listen() (net.Listener, error)

	// Serve starts serving using the provided listener.
	// Serve must start the server loop nearly immediately,
	// or at least not return any errors before the server
	// loop begins. Serve blocks indefinitely, or in other
	// words, until the server is stopped. For UDP-only
	// servers, this method can be a no-op that returns nil.
	Serve(net.Listener) error
}

TCPServer is a type that can listen and serve connections. A TCPServer must associate with exactly zero or one net.Listeners.

type UDPServer

type UDPServer interface {
	// ListenPacket starts listening by creating a new packetconn
	// and returning it. It does not start accepting connections.
	// TCP-only servers may leave this method blank and return
	// (nil, nil).
	ListenPacket() (net.PacketConn, error)

	// ServePacket starts serving using the provided packetconn.
	// ServePacket must start the server loop nearly immediately,
	// or at least not return any errors before the server
	// loop begins. ServePacket blocks indefinitely, or in other
	// words, until the server is stopped. For TCP-only servers,
	// this method can be a no-op that returns nil.
	ServePacket(net.PacketConn) error
}

UDPServer is a type that can listen and serve packets. A UDPServer must associate with exactly zero or one net.PacketConns.

Directories

Path Synopsis
basicauth
Package basicauth implements HTTP Basic Authentication for Caddy.
Package basicauth implements HTTP Basic Authentication for Caddy.
browse
Package browse provides middleware for listing files in a directory when directory path is requested instead of a specific file.
Package browse provides middleware for listing files in a directory when directory path is requested instead of a specific file.
errors
Package errors implements an HTTP error handling middleware.
Package errors implements an HTTP error handling middleware.
extensions
Package extensions contains middleware for clean URLs.
Package extensions contains middleware for clean URLs.
fastcgi
Package fastcgi has middleware that acts as a FastCGI client.
Package fastcgi has middleware that acts as a FastCGI client.
gzip
Package gzip provides a middleware layer that performs gzip compression on the response.
Package gzip provides a middleware layer that performs gzip compression on the response.
header
Package header provides middleware that appends headers to requests based on a set of configuration rules that define which routes receive which headers.
Package header provides middleware that appends headers to requests based on a set of configuration rules that define which routes receive which headers.
httpserver
Package httpserver implements an HTTP server on top of Caddy.
Package httpserver implements an HTTP server on top of Caddy.
internalsrv
Package internalsrv provides a simple middleware that (a) prevents access to internal locations and (b) allows to return files from internal location by setting a special header, e.g.
Package internalsrv provides a simple middleware that (a) prevents access to internal locations and (b) allows to return files from internal location by setting a special header, e.g.
log
Package log implements request (access) logging middleware.
Package log implements request (access) logging middleware.
markdown
Package markdown is middleware to render markdown files as HTML on-the-fly.
Package markdown is middleware to render markdown files as HTML on-the-fly.
proxy
Package proxy is middleware that proxies HTTP requests.
Package proxy is middleware that proxies HTTP requests.
redirect
Package redirect is middleware for redirecting certain requests to other locations.
Package redirect is middleware for redirecting certain requests to other locations.
rewrite
Package rewrite is middleware for rewriting requests internally to a different path.
Package rewrite is middleware for rewriting requests internally to a different path.
status
Package status is middleware for returning status code for requests
Package status is middleware for returning status code for requests
templates
Package templates implements template execution for files to be dynamically rendered for the client.
Package templates implements template execution for files to be dynamically rendered for the client.
websocket
Package websocket implements a WebSocket server by executing a command and piping its input and output through the WebSocket connection.
Package websocket implements a WebSocket server by executing a command and piping its input and output through the WebSocket connection.
Package caddytls facilitates the management of TLS assets and integrates Let's Encrypt functionality into Caddy with first-class support for creating and renewing certificates automatically.
Package caddytls facilitates the management of TLS assets and integrates Let's Encrypt functionality into Caddy with first-class support for creating and renewing certificates automatically.
storagetest
Package storagetest provides utilities to assist in testing caddytls.Storage implementations.
Package storagetest provides utilities to assist in testing caddytls.Storage implementations.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL