Intelligent URL shortening and redirect management plugin for Craft CMS 5.x with device detection, QR codes, and analytics.
- URL Shortening: Create memorable short URLs that redirect to any destination
- Device-Specific Redirects: Different URLs for iOS, Android, Huawei, Amazon, Windows, macOS, and desktop users using accurate DeviceDetector library
- Cache-Safe Device Detection: Works with CDN/static page caching (Servd, Cloudflare) by fetching fresh device detection via uncached endpoint
- Image Management: Upload and configure images with multiple size options (xl, lg, md, sm)
- QR Code Generation: Automatic QR codes for each smart link with customizable colors, styles, and logo overlay
- Landing Page Customization: Hide titles on landing pages, custom layouts, and template override support
- Advanced Analytics:
- Geographic tracking with country and city-level data
- Peak usage hours visualization
- Device, browser, and platform breakdown
- Mobile usage insights by location
- Real-time interaction tracking (auto-redirects and button clicks)
- Last interaction type and destination URL tracking
- Source tracking (QR code scan, landing page visit, or direct access)
- Button click tracking by platform (App Store, Google Play, etc.)
- Configurable analytics retention and export options
- Smart Link Field: Integrate smart links into your entries and elements
- Multi-Site Support: Different URLs per language/site (coming soon)
- User-Friendly CP: Clean interface matching Craft's design standards
- Craft CMS 5.0 or greater
- PHP 8.2 or greater
- Logging Library 5.0 or greater (installed automatically as dependency)
cd /path/to/project
composer require lindemannrock/craft-smart-links
./craft plugin/install smart-links
cd /path/to/project
ddev composer require lindemannrock/craft-smart-links
ddev craft plugin/install smart-links
In the Control Panel, go to Settings → Plugins and click "Install" for Smart Links.
IMPORTANT: After installation, you MUST generate the IP hash salt for analytics to work:
php craft smart-links/security/generate-salt
What happens if you skip this:
- ❌ Analytics tracking will fail with error:
IP hash salt not configured
- ❌ Smart links will still redirect, but won't track clicks
- ✅ You can generate the salt later, but no analytics will be collected until you do
Quick Start:
# After plugin installation:
php craft smart-links/security/generate-salt
# The command will automatically add SMART_LINKS_IP_SALT to your .env file
# Copy this value to staging/production .env files manually
Navigate to Settings → Smart Links in the control panel to configure:
- Enable Analytics: Global toggle to enable/disable all analytics functionality (disables all tracking and hides analytics UI when off)
- Analytics Retention: How many days to keep analytics data (0 for unlimited)
- Export Settings: Include/exclude disabled smart links in analytics exports
- QR Code Settings: Default size, colors, and format (individual links can override)
- Redirect Settings: Language detection method and 404 redirect URL
- Interface Settings: Items per page in element index
Create a config/smart-links.php
file to override default settings:
<?php
return [
// Plugin Settings
'pluginName' => 'Smart Links',
// Logging Settings
'logLevel' => 'error', // error, warning, info, or debug (debug requires devMode)
// URL Settings
'slugPrefix' => 'go', // URL prefix for smart links (e.g., 'go' creates /go/your-link)
'qrPrefix' => 'qr', // URL prefix for QR code pages (e.g., 'qr' creates /qr/your-link)
// Analytics Settings
'enableAnalytics' => true,
'analyticsRetention' => 90, // days (0 for unlimited)
'includeDisabledInExport' => false,
'includeExpiredInExport' => false,
'enableGeoDetection' => false,
// QR Code Settings
'defaultQrSize' => 256,
'defaultQrColor' => '#000000',
'defaultQrBgColor' => '#FFFFFF',
'defaultQrFormat' => 'png', // or 'svg'
'defaultQrErrorCorrection' => 'M', // L, M, Q, H
'defaultQrMargin' => 4,
'qrModuleStyle' => 'square', // square, rounded, dots
'qrEyeStyle' => 'square', // square, rounded, leaf
'qrEyeColor' => null, // null = use main color
'enableQrLogo' => false,
'qrLogoSize' => 20, // percentage (10-30%)
'defaultQrLogoId' => null,
'enableQrDownload' => true,
'qrDownloadFilename' => '{slug}-qr-{size}',
'qrCodeCacheDuration' => 86400, // seconds
// Template Settings
'redirectTemplate' => null, // e.g., 'smart-links/redirect'
'qrTemplate' => null, // e.g., 'smart-links/qr'
// Device Detection & Caching
'cacheDeviceDetection' => true,
'deviceDetectionCacheDuration' => 3600, // seconds
// Language & Redirect Settings
'languageDetectionMethod' => 'browser', // 'browser', 'ip', or 'both'
'notFoundRedirectUrl' => '/',
// Interface Settings
'itemsPerPage' => 100,
// Site Selection
'enabledSites' => [], // Array of site IDs, empty = all sites
// Multi-environment support
'dev' => [
'logLevel' => 'debug', // More verbose in dev
'enableAnalytics' => true,
'analyticsRetention' => 30,
],
'production' => [
'logLevel' => 'error', // Only errors in production
'enableAnalytics' => true,
'analyticsRetention' => 365,
'cacheDeviceDetection' => true,
'deviceDetectionCacheDuration' => 7200,
],
];
Important: After changing slugPrefix
or qrPrefix
, clear Craft's routes cache:
php craft clear-caches/compiled-templates
See Configuration Documentation for all available options.
Smart Links respects Craft's allowAdminChanges
setting for production environments. When allowAdminChanges
is disabled:
- All settings pages display in read-only mode with a notice banner
- Field layout designer is disabled
- Save actions return 403 Forbidden errors
- Config file settings override database settings
Enable read-only mode in your .env
:
CRAFT_ALLOW_ADMIN_CHANGES=false
This ensures settings and field layouts can only be modified through:
- Project config (synced across environments)
- Configuration files (
config/smart-links.php
)
Best Practice: Use allowAdminChanges=false
in staging/production to prevent direct CP modifications and ensure consistency across environments.
Smart Links supports restricting functionality to specific sites in multi-site installations.
Configure which sites Smart Links should be enabled for:
Via Control Panel:
- Go to Settings → Plugins → Smart Links → General
- Check the sites where Smart Links should be available
- Leave empty to enable for all sites
Via Configuration File:
// config/smart-links.php
return [
'enabledSites' => [1, 2], // Only enable for sites 1 and 2
// Environment-specific overrides
'dev' => [
'enabledSites' => [1], // Only main site in development
],
'production' => [
'enabledSites' => [1, 2, 3], // All sites in production
],
];
Behavior:
- CP Navigation: Smart Links only appears in sidebar for enabled sites
- Site Switcher: Only enabled sites appear in the site dropdown
- Access Control: Direct access to disabled sites returns 403 Forbidden
- Backwards Compatibility: Empty selection enables all sites
Important Notes:
- If the primary site is not included in
enabledSites
, Smart Links will not appear in the main CP navigation at all, as the navigation uses the primary site context. Ensure you include your primary site ID if you want Smart Links accessible from the main menu. - You can still access Smart Links on enabled non-primary sites via direct URLs, but the main navigation will be hidden.
- Navigate to Smart Links in the control panel
- Click "New smart link"
- Fill in the required fields:
- Name: Internal reference name
- Slug: The short URL path (e.g.,
promo-2024
) - Desktop URL: Where desktop users go
- Mobile URL: Where mobile users go (optional)
- Tablet URL: Where tablet users go (optional)
Your smart links will be accessible at:
https://yourdomain.com/go/[slug]
- Redirect URLhttps://yourdomain.com/qr/[slug]
- QR code imagehttps://yourdomain.com/qr/[slug]/view
- QR code display page
Customizable URL Prefixes:
You can customize the /go/
and /qr/
prefixes via Settings → General or Settings → QR Code:
- Change
slugPrefix
fromgo
tolink
,s
, or any custom prefix - Change
qrPrefix
fromqr
toqrcode
,code
, or any custom prefix - Only letters, numbers, hyphens, and underscores are allowed
- After changing, clear routes cache:
php craft clear-caches/compiled-templates
Add a Smart Link field to any element:
- Go to Settings → Fields
- Create a new Smart Link field
- Add it to your field layout
In templates:
{# Get the smart link #}
{% set smartLink = entry.mySmartLinkField.one() %}
{# Output the redirect URL #}
<a href="{{ smartLink.getRedirectUrl() }}">{{ smartLink.name }}</a>
{# Get the QR code URL #}
<img src="{{ smartLink.getQrCodeUrl() }}" alt="QR Code">
{# Check if QR is enabled #}
{% if smartLink.qrCodeEnabled %}
<img src="{{ siteUrl('qr/' ~ smartLink.slug) }}" alt="QR Code">
{% endif %}
Smart Links provides comprehensive analytics dashboard with interaction tracking:
Navigate to Smart Links → Analytics to see:
- Total interactions (auto-redirects and button clicks)
- Active links and links used percentage
- Daily interaction trends chart organized by sections:
- Traffic Overview: Daily interactions visualization
- Device & Platform Analytics: Separate charts for device types and operating systems
- Geographic Distribution: Top countries and cities side by side
- Usage Patterns: Peak hours and behavioral insights
- Top Smart Links table showing:
- Total interactions count
- Last interaction timestamp and type (redirect or button click)
- Last destination URL (truncated to 25 characters)
- QR code scans vs direct visits breakdown
- Latest Interactions table with detailed tracking:
- Interaction type (redirect or button click)
- Button type (App Store, Google Play, etc.) for button clicks
- Source (QR scan, landing page, or direct access)
- Destination URL, device info, OS, and location
- Top Countries: See which countries your traffic comes from
- Top Cities: City-level breakdown with click percentages
- View Geographic Details: Comprehensive modal showing all countries and cities
- Peak Usage Hours: Hourly bar chart showing when users are most active
- Mobile Usage by City: See mobile vs desktop preferences by location
- Browser Preferences: Most popular browsers by country
- Date range filtering with AJAX updates (Last 7 days, 30 days, 90 days, All time)
- Export analytics data to CSV with configurable options
- Real-time interaction tracking (auto-redirects and button clicks only)
- Privacy-focused IP hashing with secure salt (rainbow-table proof)
- Automatic geographic detection using ip-api.com
- Global analytics toggle to disable all tracking
- Per-link analytics control with confirmation prompts
Smart Links uses salted SHA256 hashing for IP addresses to prevent rainbow table attacks and protect visitor privacy:
Setup (Local/Dev Environment Only):
-
Generate a salt in your local/dev environment:
php craft smart-links/security/generate-salt
This automatically adds
SMART_LINKS_IP_SALT
to your.env
file. -
Copy the salt to staging/production:
- Open your local
.env
file - Copy the
SMART_LINKS_IP_SALT
value - Manually add it to staging and production
.env
files - DO NOT regenerate the salt in staging/production
Example:
# Local .env SMART_LINKS_IP_SALT="0dffabe583a420819eba489d4c54f81aca4d6d8260f8188833a619127fab2646" # Copy this EXACT value to: # - staging/.env # - production/.env
- Open your local
Critical Requirements:
⚠️ Generate ONCE in local/dev environment only⚠️ Use the SAME salt across all environments (dev, staging, production)- ❌ Never regenerate in staging/production - this will break analytics
- ❌ Never commit the salt to version control (add
.env
to.gitignore
) - ✅ Store the salt securely (password manager recommended)
⚠️ Changing the salt will reset unique visitor tracking- ✅ Salt is required for analytics tracking to work
- ✅ Raw IP addresses are NEVER stored (only salted hash + geo-location data)
Privacy Features:
- ✅ Rainbow table proof - Salted SHA256 hashing
- ✅ No raw IP storage - IP removed from metadata after geo-lookup
- ✅ Geo-location preserved - Country, city, region stored separately
- ✅ GDPR compliant - No personally identifiable IP data retained
- ✅ Unique visitor tracking - Maintained via salted hashes
- ✅ Optional IP anonymization - Extra privacy layer (see below)
For maximum privacy, you can enable IP address anonymization. This masks IPs before hashing and geo-location:
How it works:
- IPv4: Masks last octet (
192.168.1.123
→192.168.1.0
) - IPv6: Masks last 80 bits (keeps first 48 bits for ISP/network)
- IP is anonymized BEFORE hashing and geo-lookup
- Even if salt leaks, attackers only get subnet information
Enable via Settings:
- Go to Settings → Smart Links → Analytics
- Enable Anonymize IP Addresses
Or via Config:
// config/smart-links.php
return [
'anonymizeIpAddress' => true,
];
Trade-offs:
- ✅ Extra Privacy: Subnet-level anonymization + salt
- ✅ Geo-location: Still works (city/country detection unaffected)
⚠️ Accuracy: Multiple users on same subnet = counted as one visitor⚠️ Corporate/Office Networks: All users appear as single visitor
When to use:
- High-privacy requirements (government, healthcare, EU)
- GDPR/privacy law compliance requirements
- Don't need precise unique visitor counts
- Prefer privacy over analytics accuracy
Privacy Levels Comparison:
Feature | Default (Salt Only) | With Anonymization |
---|---|---|
Privacy | Rainbow-table proof | Maximum (subnet-level) |
Unique Visitors | Very accurate (per IP) | Less accurate (per subnet) |
Geo-location | ✅ Works | ✅ Works |
Corporate Networks | Each user = unique | All users = one visitor |
Salt Leak Risk | Reveals full IP | Only reveals subnet |
Smart Links can integrate with third-party analytics and tracking services to push click events beyond its built-in analytics.
When SEOmatic is installed, Smart Links can push click events to Google Tag Manager's data layer for tracking in GTM and Google Analytics.
Setup:
- Install and configure SEOmatic plugin with GTM or Google Analytics
- Navigate to Settings → Smart Links → Analytics
- Scroll to Third-Party Integrations section
- Enable SEOmatic Integration
- Select which events to track (redirects, button clicks, QR scans)
- Customize the event prefix if needed (default:
smart_links
) - Save settings
GTM Event Structure:
Events are pushed to window.dataLayer
with the following structure:
{
event: "smart_links_redirect",
smart_link: {
slug: "promo-2024",
title: "Summer Promo",
destination_url: "https://app.example.com/promo",
platform: "ios", // ios, android, windows, macos, other
source: "qr", // qr, landing, direct
device_type: "mobile", // mobile, tablet, desktop
os: "iOS 17",
browser: "Safari",
country: "United States",
city: "New York",
click_type: "button" // button or redirect
}
}
Event Types:
smart_links_redirect
- Auto-redirects (mobile users automatically redirected)smart_links_button_click
- Button clicks (manual platform selection)smart_links_qr_scan
- QR code scans (accessed via?src=qr
parameter)
GTM Trigger Setup:
Create triggers in Google Tag Manager to listen for these events:
- Trigger Type: Custom Event
- Event Name:
smart_links_redirect
(or your custom prefix) - Use regex matching to catch all Smart Links events:
smart_links_.*
GA4 Event Example:
Forward Smart Links events to Google Analytics 4:
Event Name: smart_link_click
Parameters:
- link_slug: {{smart_link.slug}}
- link_platform: {{smart_link.platform}}
- link_source: {{smart_link.source}}
- device_type: {{smart_link.device_type}}
Configuration via Config File:
// config/smart-links.php
return [
'enabledIntegrations' => ['seomatic'],
'seomaticTrackingEvents' => ['redirect', 'button_click', 'qr_scan'],
'seomaticEventPrefix' => 'smart_links',
];
Important Notes:
- Events are only sent when analytics tracking is enabled (globally and per-link)
- Requires SEOmatic plugin to be installed and enabled
- GTM or Google Analytics must be configured in SEOmatic
- Events include all analytics data Smart Links already tracks
- No additional external API calls or performance impact
Template Usage:
Add the tracking method to your templates to enable client-side event tracking:
{# templates/smart-links/redirect.twig #}
<!DOCTYPE html>
<html>
<head>
<title>{{ smartLink.title }}</title>
{# Render SEOmatic tracking script (outputs JavaScript if enabled, null if disabled) #}
{{ smartLink.renderSeomaticTracking('redirect') }}
</head>
<body>
{# Your template content #}
</body>
</html>
For QR code templates:
{# templates/smart-links/qr.twig #}
{{ smartLink.renderSeomaticTracking('qr_scan') }}
Event Types:
'redirect'
- Use for landing pages with buttons and auto-redirects'qr_scan'
- Use for QR code display pages
How It Works:
- The method returns client-side JavaScript that pushes events to
window.dataLayer
- Returns
null
if SEOmatic is not installed or disabled (no output) - No need for
|raw
filter (returns\Twig\Markup
automatically) - Button clicks are intercepted with 300ms delay to ensure tracking completes
- Works with debug mode: add
?debug=1
to test tracking without redirects
Each smart link automatically generates a QR code:
- Access at:
https://yourdomain.com/qr/[slug]
- Customize size:
?size=300
- Customize colors:
?color=FF0000&bg=FFFFFF
- Returns PNG or SVG image based on settings
- Configurable default colors and size in settings
- Per-link QR code customization with reset button
- Live preview in edit page (280px, bottom-right corner)
- Logo overlay support (PNG format only, not available with SVG)
- Advanced styling options (module style, eye style, eye color)
Each smart link has its own settings:
- Custom size, colors per link
- Reset to defaults button in sidebar
- Live preview in edit page
- Toggle analytics tracking per link
- Confirmation prompt when disabling
- Respects global analytics setting
When smart links are trashed:
- They are no longer accessible via their URLs
- They cannot be edited until restored
- They appear in the trashed status filter
- Analytics data is preserved until permanent deletion
Smart Links requires custom templates for the redirect landing page and QR code display. These templates must be created in your project's templates/
directory.
When redirectTemplate
and qrTemplate
are not configured (set to null
), the plugin looks for:
- Redirect landing page:
templates/smart-links/redirect.twig
- QR code display:
templates/smart-links/qr.twig
Important: If these templates don't exist, visitors will see a "Unable to find template" error when accessing your smart links.
The plugin includes example templates you can copy to get started:
# Create templates directory
mkdir -p templates/smart-links
# Copy example templates
cp vendor/lindemannrock/smart-links/src/templates/redirect.twig templates/smart-links/
cp vendor/lindemannrock/smart-links/src/templates/qr.twig templates/smart-links/
# Customize the templates to match your site's design
You can use different template paths by configuring them:
Via Config File:
// config/smart-links.php
return [
'redirectTemplate' => 'my-custom/landing', // Path relative to templates/
'qrTemplate' => 'my-custom/qr-display', // Path relative to templates/
];
Via Control Panel: Settings → Redirect Settings → Custom Redirect Template Settings → QR Code → Custom QR Code Template
Basic Template Example:
{# templates/smart-links/redirect.twig #}
<!DOCTYPE html>
<html>
<head>
<title>{{ smartLink.title }}</title>
{# SEOmatic tracking integration (if enabled) #}
{{ smartLink.renderSeomaticTracking('redirect') }}
<script>
// Client-side mobile detection for auto-redirect (works with cached pages)
(function() {
fetch('{{ actionUrl('smart-links/redirect/refresh-csrf')|raw }}', {
credentials: 'same-origin',
cache: 'no-store'
})
.then(r => r.json())
.then(data => {
if (data.isMobile) {
window.location.replace('{{ actionUrl('smart-links/redirect/go', {slug: smartLink.slug, platform: 'auto'})|raw }}');
}
})
.catch(err => {
console.error('Device detection failed:', err);
});
})();
</script>
</head>
<body>
<h1>{{ smartLink.title }}</h1>
{# Platform-specific buttons that track clicks via redirect controller #}
{% if smartLink.iosUrl %}
<a href="{{ actionUrl('smart-links/redirect/go', {slug: smartLink.slug, platform: 'ios', site: smartLink.site.handle}) }}">Download on App Store</a>
{% endif %}
{% if smartLink.androidUrl %}
<a href="{{ actionUrl('smart-links/redirect/go', {slug: smartLink.slug, platform: 'android', site: smartLink.site.handle}) }}">Get it on Google Play</a>
{% endif %}
{# Fallback button #}
<a href="{{ actionUrl('smart-links/redirect/go', {slug: smartLink.slug, platform: 'fallback', site: smartLink.site.handle}) }}">Continue to Website</a>
{# QR Code #}
{% if smartLink.qrCodeEnabled %}
<img src="{{ smartLink.getQrCodeUrl() }}" alt="QR Code">
{% endif %}
</body>
</html>
How Tracking Works:
The tracking system uses the redirect controller to log all interactions:
- Mobile auto-redirects: JavaScript detects mobile and redirects via
platform: 'auto'
- Button clicks: All buttons use
actionUrl('smart-links/redirect/go')
which tracks before redirecting - QR code scans: QR codes include
?src=qr
parameter for source tracking - Works with CDN caching: Device detection happens client-side via uncached endpoint
- Desktop page loads: Not tracked unless a button is clicked
Available Template Variables:
smartLink
- The SmartLink elementdevice
- DeviceInfo object with detection resultsredirectUrl
- The calculated redirect URL for current devicelanguage
- Detected language code
Create a custom QR code display page:
Template Example:
{# templates/smart-links/qr.twig #}
<!DOCTYPE html>
<html lang="{{ currentSite.language }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ smartLink.title }} - QR Code</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div class="flex justify-center items-center max-w-lg mx-auto px-4 py-8">
<div class="bg-white rounded-2xl shadow-lg max-w-lg w-full p-8 text-center">
<h1 class="text-3xl font-semibold mb-4">{{ smartLink.title }}</h1>
{% if smartLink.description %}
<p class="text-gray-600 mb-8">{{ smartLink.description }}</p>
{% endif %}
{# QR Code display #}
<div class="my-8 mx-auto">
<img src="{{ smartLink.getQrCodeUrl({ size: size ?? 300 }) }}"
alt="{{ smartLink.title }} QR Code"
class="mx-auto">
</div>
<p class="text-sm text-gray-600">Scan with your phone's camera</p>
</div>
</div>
</body>
</html>
Available QR Template Variables:
smartLink
- The SmartLink elementsize
- QR code size from URL parameterformat
- QR code format from URL parametercolor
- QR code color from URL parameterbg
- QR code background color from URL parameter
smartLink.id
smartLink.name
smartLink.slug
smartLink.title
smartLink.description
smartLink.fallbackUrl
smartLink.iosUrl
smartLink.androidUrl
smartLink.huaweiUrl
smartLink.amazonUrl
smartLink.windowsUrl
smartLink.macUrl
smartLink.enabled {# Per-site status managed by Craft's element system #}
smartLink.trackAnalytics
smartLink.qrCodeEnabled
smartLink.hideTitle
smartLink.getImage()
smartLink.imageSize
smartLink.clicks {# Dynamically calculated from analytics data #}
smartLink.dateCreated
smartLink.dateUpdated
Note:
enabled
is a per-site property managed by Craft CMS's element system (stored inelements_sites
table)clicks
is dynamically calculated by counting records in thesmartlinks_analytics
table
device.isMobile
device.isTablet
device.isDesktop
device.platform {# ios, android, huawei, windows, macos, linux, other #}
device.deviceType
device.brand
device.osName
device.osVersion
device.browser
device.language
{# Get the appropriate redirect URL for current device #}
smartLink.getRedirectUrl()
{# Get QR code URL #}
smartLink.getQrCodeUrl(size = 200)
{# Get QR code display page URL #}
smartLink.getQrCodeDisplayUrl()
{# Get full URL #}
smartLink.getUrl()
{# Get image asset #}
smartLink.getImage()
{# Render SEOmatic tracking script (returns Twig\Markup or null) #}
smartLink.renderSeomaticTracking(eventType = 'qr_scan')
{# Event types: 'redirect' for landing pages, 'qr_scan' for QR code pages #}
query {
smartLinks {
id
name
slug
desktopUrl
mobileUrl
enabled
clicks
}
}
use lindemannrock\smartlinks\events\RedirectEvent;
use lindemannrock\smartlinks\services\RedirectService;
use yii\base\Event;
Event::on(
RedirectService::class,
RedirectService::EVENT_BEFORE_REDIRECT,
function(RedirectEvent $event) {
// Modify redirect URL
if ($event->device === 'mobile') {
$event->url = 'https://m.example.com';
}
}
);
# Generate secure salt for IP hashing (required for analytics)
./craft smart-links/security/generate-salt
Note: Analytics backfill commands (update-countries
, update-cities
) were removed in v5.0.2+. Geo-location now happens at tracking time only.
Smart Links uses the LindemannRock Logging Library for centralized, structured logging across all LindemannRock plugins.
- Error: Critical errors only (default)
- Warning: Errors and warnings
- Info: General information
- Debug: Detailed debugging (includes performance metrics, requires devMode)
// config/smart-links.php
return [
'logLevel' => 'error', // error, warning, info, or debug
];
Note: Debug level requires Craft's devMode
to be enabled. If set to debug with devMode disabled, it automatically falls back to info level.
- Location:
storage/logs/smart-links-YYYY-MM-DD.log
- Retention: 30 days (automatic cleanup via Logging Library)
- Format: Structured JSON logs with context data
- Web Interface: View and filter logs in CP at Smart Links → Logs
Access logs through the Control Panel:
- Navigate to Smart Links → Logs
- Filter by date, level, or search terms
- Download log files for external analysis
- View file sizes and entry counts
- Auto-cleanup after 30 days (configurable via Logging Library)
Requires: lindemannrock/logginglibrary
plugin (installed automatically as dependency)
See docs/LOGGING.md for detailed logging documentation.
- Ensure GD or ImageMagick is installed
- Check file permissions on
storage/runtime/
- Verify QR codes are enabled in settings
- Check
.htaccess
or nginx config allows/go/
URLs - Ensure smart link is enabled
- Verify URLs are properly formatted
Mobile users will briefly see the landing page before being automatically redirected:
- All users (mobile and desktop) see the landing page with JavaScript
- JavaScript fetches fresh device detection from
/smart-links/redirect/refresh-csrf
(uncached endpoint) - If mobile device is detected, JavaScript redirects via
actionUrl('smart-links/redirect/go', {platform: 'auto'})
- Desktop users stay on the landing page with platform buttons
This client-side approach ensures tracking works correctly even when pages are cached by CDN (Servd, Cloudflare).
Troubleshooting:
- Ensure mobile detection script is in your template (fetches from
refresh-csrf
endpoint) - Ensure
/smart-links/redirect/refresh-csrf
endpoint is not being cached - Check browser console for errors
- The brief landing page flash is normal and necessary for tracking to work with page caching
- Confirm analytics is enabled globally in Settings → General
- Verify per-link analytics is enabled for the smart link
- Ensure buttons use
actionUrl('smart-links/redirect/go')
instead of direct URLs - Check browser isn't blocking JavaScript
- Check browser console for errors
- Desktop page loads without
?src=qr
parameter are intentionally NOT tracked
When running locally (DDEV, localhost), analytics will default to Saudi Arabia because local IPs can't be geolocated. To set your actual location for testing:
# Add to your .env file:
SMART_LINKS_DEFAULT_COUNTRY=AE
SMART_LINKS_DEFAULT_CITY=Dubai
Supported locations:
- UAE: Dubai, Abu Dhabi
- Saudi Arabia: Riyadh, Jeddah
Multi-site support is coming soon. Future features will include:
- Different URLs per site/language
- Separate slugs for each locale
- Per-site analytics tracking
- Propagation settings
- Documentation: https://github.com/LindemannRock/craft-smart-links
- Issues: https://github.com/LindemannRock/craft-smart-links/issues
- Email: support@lindemannrock.com
This plugin is licensed under the MIT License. See LICENSE for details.
Developed by LindemannRock