CSV Import¶
Short URL supports bulk-creating short URLs from a CSV file, via an admin form or a Drush command.
CSV format¶
The CSV file uses positional columns in this order:
| Column | Required | Default | Description |
|---|---|---|---|
slug |
Custom mode only | — | The vanity slug. Ignored for base36 and auto-increment modes. |
destination |
Yes | — | External URL or internal path (/about, stored as internal:/about) |
slug_mode |
No | base36 |
custom, base36, or auto_increment |
label |
No | — | Optional human-readable label; an update without one keeps the short URL's label |
langcode |
No | Site default | Language code (e.g. en, fr); an update without one keeps the short URL's language |
redirect_status |
No | Site default | 301, 302, or 307; an update without one keeps the short URL's status |
Columns that other modules add come after these (see Columns from other modules).
Example¶
slug,destination,slug_mode,label,langcode,redirect_status
promo,https://example.com/spring-sale,custom,Spring Sale,en,301
,https://example.com/page1,base36,,,
,https://example.com/page2,auto_increment,,,
Format auto-detection¶
When uploading a CSV file, the importer automatically detects known formats by matching the header row. Currently supported:
| Format | Expected headers | Column mapping |
|---|---|---|
| YOURLS | source, target, hits |
source → slug, target → destination |
Detection is BOM-safe (UTF-8 BOM is stripped) and tolerates extra trailing columns beyond the expected headers. A BOM is also stripped from files without a recognized header.
When a format is detected, a format defaults step appears
where you can configure default values for columns not present
in the source file (e.g. language, redirect status). Some values
may be enforced by the format — for example, YOURLS enforces
slug_mode=custom since all YOURLS URLs have custom slugs.
Other modules can register additional formats via
hook_shorturl_csv_formats_alter(). See ShortUrlCsvFormatInterface
for the format contract.
Updating existing short URLs¶
When importing slugs that already exist in the database, the importer can update the existing short URL instead of skipping the row. This is controlled by:
- Admin form: the "Update existing short URLs" checkbox (enabled by default)
- Drush: the
--update-existingflag (enabled by default, disable with--no-update-existing)
In the preview step, the Action column shows whether each row will Create a new short URL or Update an existing one. The summary and import button also display separate counts.
An update sets the destination and slug mode from the row. An empty label, language or redirect status keeps the short URL's own, so re-importing a file without those columns, such as a YOURLS export, changes nothing else. The preview shows Unchanged for the label, language and redirect status.
Admin form¶
Navigate to Content > Short URLs and click the Import short
URLs action link, or go directly to
/admin/content/short-urls/import.
Step 1: Upload¶
- Select a CSV file (
.csvor.txt, max 10 MB) - Choose the delimiter (comma, semicolon, or tab)
- Check "Skip first row" if the file has a header row
- Check "Update existing short URLs" to update rows with existing slugs instead of skipping them
- Click Preview
Step 1.5: Format defaults (auto-detected formats only)¶
When a known format is detected (e.g. YOURLS), this step appears to let you configure defaults for unmapped columns:
- Slug mode — enforced by YOURLS (custom), editable for other formats
- Language — none by default: new short URLs get the site default language, and short URLs being updated keep their own
- Redirect status — none by default: new short URLs get the site-wide redirect status, and short URLs being updated keep their own
Click Preview to continue or Back to change the file.
Step 2: Preview and confirm¶
The form displays:
- A summary of valid and invalid rows with create/update counts
- A table listing each valid row with its columns and action
- Error details for invalid rows (with line numbers)
Rows with errors are skipped during import. Click Import to create or update nodes for all valid rows via Batch API.
Permission required: Import short URLs from CSV. Each row also needs what the node form needs: Short URL: Create new content to create a short URL, and the right to edit the short URL a row updates (Short URL: Edit any content, or Short URL: Edit own content for one's own).
Drush command¶
drush shorturl:import /path/to/urls.csv --user=editor
Known CSV formats (e.g. YOURLS) are automatically detected from the header row. When a format is detected, columns are mapped automatically and the header is skipped.
The import runs as the account --user names, by ID or name, and
refuses to start without one. Rows are checked against that account's
permissions, and the short URLs the import creates belong to it.
Naming user 1 bypasses every permission check.
Options¶
| Option | Default | Description |
|---|---|---|
--user |
none | Account to import as (required) |
--delimiter |
, |
CSV delimiter character |
--skip-header |
No | Skip the first row |
--update-existing |
Yes | Update existing slugs instead of skipping |
--dry-run |
No | Validate only, do not create or update nodes |
Examples¶
# Basic import
drush shorturl:import urls.csv --user=editor --skip-header
# Import a YOURLS export (auto-detected)
drush shorturl:import yourls_export.csv --user=editor
# Semicolon-delimited file
drush shorturl:import urls.csv --user=editor --delimiter=";" --skip-header
# Skip existing slugs instead of updating
drush shorturl:import urls.csv --user=editor --no-update-existing
# Validate without importing
drush shorturl:import urls.csv --user=editor --skip-header --dry-run
The command displays validation results, asks for confirmation when errors are found, and shows a progress log during import.
Validation rules¶
Each row is validated before import:
- Destination is required and must be a valid URL (external)
or internal path (starting with
/) - Slug is required for custom mode, must contain only
a-z,0-9,_, and-, and cannot be a path the site already answers (the same rules as the node form); the slug of a short URL being updated is kept as it is - Slug uniqueness is checked against other rows in the same CSV. Existing slugs in the database are either marked for update or rejected, depending on the update-existing setting
- Slug mode must be
custom,base36, orauto_increment - Permission — the importing user must have the permission for the slug mode used in each row, and may only create a short URL, or update an existing one, as the node form would let them
- Language must be an installed language
- Redirect status must be 301, 302, or 307
- Columns from other modules are validated by the module that adds them
Columns from other modules¶
Other modules can add columns to the import. They follow Short URL's own columns, and a detected format can map a source column to them. Each module validates its column, can scope slug uniqueness by it, and sets its value on the short URL.
Domain Short URL
adds a domain column this way, as the seventh column. Each row
can name a domain machine name to put the short URL on that domain,
and slug uniqueness is then checked per domain:
slug,destination,slug_mode,label,langcode,redirect_status,domain
blog,https://example.com/blog,custom,Blog,en,301,example_com
To add a column, implement
Drupal\shorturl\ShortUrlCsvColumnInterface and tag the service
with shorturl.csv_column:
services:
Drupal\my_module\MyCsvColumn:
tags:
- { name: shorturl.csv_column }