These are some common issues that people have reported when using Shlink, together with questions that people ask with a higher frequency.
When you install Shlink using the installation tool, you will be able to easily provide all the config options.
However, sometimes you need to change some of those params later in time.
In order to do that, you can simply use the
vendor/bin/shlink-installer set-option script to change the value for any config option.
If that script fails, it could mean you are using a too old Shlink version. In that case, follow these steps:
- Make a backup of the generated config file (
- Open that same file, locate the config option you want to change, and set the new value.
- Delete the config cache file (
data/cache/app_config.php). If the file does not exist, skip this step.
The generated config file is in PHP format, but it’s a relatively straightforward options “map”.
If you are not sure which option should be changed, feel free to open an issue asking for help.
If Shlink does not work after changing the config, restore the backup you did in first step.
If you have recently updated Shlink, it could include some new code that is not picked due to in-memory caches.
php-fpm services. It should solve the problem.
First, shared hosting is not the main target env for Shlink. The goal of the project is to provide a cloud-native and cloud-friendly solution to self-host a URL shortener.
There are other projects which play better with shared hosting. Try googling “self-hosted URL shortener”.
That said, there are ways to get Shlink working.
- Some shared hosting services provide SSH access to the document root’s parent folder. If that’s the case, upload Shlink there, and then rename the
publicfolder to whatever name your service provider is using for the document root.
- If you only have access to the document root, upload the project there, and then copy everything inside the
publicfolder to the project’s root. Sadly, this makes all other folders accessible. It’s highly recommended that you deny access to them if you shared hosting provider allows it, for example, using
In any case, you will need to have some kind of shell access in order to run the installation tool.
Shlink does not directly depend on the name of the folder, it’s perfectly fine to rename it and continue working.
However, Shlink generates a cached config file that resolves absolute paths. That makes file paths defined on it to still point to the old folder name, making Shlink unusable.
The solution is to just delete that file,
data/cache/app_config.php. After that, Shlink will regenerate it with the new folder name where needed.
It’s relatively frequent that servers have many PHP versions coexisting, being one of them the default one.
Sometimes, you think you are using the latest version required by Shlink, because that’s what you see with
phpinfo() executed on a web request.
When this happens, it is because you are running an older PHP version from the CLI. Run
php -v to see the actual version being used.
Sometimes, after setting up Shlink and verifying it works from the command line, and you can create and visit short URLs, you try to get it configured on a web client like shlink-web-client, only to realize it fails to connect, displaying an error page.
In most of the cases, this is due to one of these other two issues:
- Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present
- I see “Mixed Content” errors in the browser’s console
In order to find out which is the actual root cause, you will have to open the browser’s developer console, which will most probably display one of the errors above.
These are the instructions to open it on the most popular web browsers:
Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present
Shlink generates all the CORS headers required to consume it from a web app running on different domains, including
It also handles transparently the preflight (
OPTIONS) requests (See more info on the topic).
When the error above happens when consuming Shlink from shlink-web-client or any other web client running on a different URL, that’s because some element in between is dropping the CORS headers or blocking the
The most frequent candidate is some reversed proxy with strict security rules. You need to make sure any reverse proxy in front of your Shlink instance returns transparently all the headers generated by Shlink.
Another scenario in which this could happen is when accessing Shlink from a web client which was accessed through IP address instead of hostname. This scenario is fixed after Shlink 2.5.2, but could also be the cause when using previous versions.
Many browsers (like Google Chrome on its latest versions) do not allow requests to
http endpoints from web apps that were served with
If you are trying to access a Shlink instance which is served with
http (no encryption) from the default shlink-web-client at https://app.shlink.io or any other web client which is served with
https, you will see those errors.
It is highly recommended that you serve Shlink with
https to solve this problem, but instead, you can make sure the web client is also served with
In order to properly resolve short URLs when they use a non-default domain, Shlink needs to know what’s the host to which the request was made.
If you have a reverse proxy in front of Shlink, make sure to forward the hostname in the
For example when using nginx as a reverse proxy, use the
proxy_set_header Host $host; instruction.
You can also get detailed info on how to expose Shlink through a reverse proxy.
There are two main reasons for the visits to never get located:
- You are serving Shlink with a classic web server (like nginx or apache) and didn’t schedule the task that processes the visits.
- You didn’t provide your own GeoLite2 license key.
When the second happens, Shlink falls back to the default license key. The problem is that this key is used by too many people, and the daily download limit is always hit.
Using your own GeoLite2 license key is currently considered mandatory. Follow the instructions to get it.
First, real-time updates are only triggered when serving Shlink with openswoole (or when using the docker image, which uses openswoole internally), and they will never work if you serve it with a classic web server.
Second, real-time updates are triggered after a visit has been located. Because of this, the issue described in previous point can also result in the updates never getting triggered.
For example, if you do not provide your own GeoLite2 license key and the default one already reached the limit, you will never get your first GeoLite2 db file, never being able to locate visits, and finally, never getting the real-time updates triggered.
Follow the instructions to provide your own GeoLite2 license key.
The database user you provide to Shlink will need to have at least these grants:
Shlink’s docker image comes with a SQLite database inside it. This database is intended just for testing and experimentation purposes.
As soon as you plan to use Shlink in production, you should always use an external database, as that’s the only way to ensure you will not loose any data if the container gets killed.
Some people have tried to mount the SQLite database file in a volume, but that is not possible see this comment to understand why.
Yes, that’s right. Handling the https certificate is out of Shlink’s scope.
The recommendation is to put a reverse proxy that provides this capability in front of Shlink.
Follow these instructions in order to know how to expose Shlink through a reverse proxy.
Sometimes, when running Shlink’s docker image, the container may fail to boot-up, without a clear or obvious reason.
A good way to troubleshoot this is by providing the env var
SHELL_VERBOSITY=3, that will increase verbosity of the image’s entry point.
Subscription from shlink-web-client to mercure fails with “Response to preflight request doesn’t pass access control check”
If you are serving your mercure instance on a host different from the one where you serve shlink-web-client (which is most probably the case), you need to ensure mercure allows the client’s host.
For mercure older than v0.11, that’s done via the
CORS_ALLOWED_ORIGINS env var, which can have the
"*" value, or the explicit domain of your web client.
For mercure v0.11 or newer, you need to pass the
MERCURE_EXTRA_DIRECTIVES env var, with value
cors_origins https://my-web-client.com. This versions no longer allows the
Shlink’s installation tool does a couple of things. One of them is writing a config file with provided options.
A common mistake is running the installation tool from the wrong directory, which results on the generated config file’s path not being found, which triggers an error like this:
Error writing to "config/params/generated_config.php": file_put_contents(config/params/generated_config.php): Failed to open stream: No such file or directory.
In order to solve this, make sure the working directory when running the installation tool, is always Shlink’s root folder.