Opening remote files in LibreOffice on Windows¶
This guide walks through giving your users a one-click "Open in LibreOffice" button that hands off any remote URL — WebDAV, plain HTTPS, whatever LibreOffice can read — from the browser to LibreOffice on Windows. The recipe is generic; this page lives in the webdav module's docs because that's where the question usually arises, but nothing below is WebDAV-specific until you reach the troubleshooting notes at the very end.
Why a custom URL scheme is needed¶
LibreOffice on Windows declares a handful of URL schemes via its
installer (vnd.libreoffice.command:, ms-word:, etc.), but
those schemes invoke UNO commands inside an already-running
LibreOffice — they aren't suitable for the "open this remote
document URL" use case. To hand an arbitrary https://… URL to
soffice.exe, the simplest path is to register your own URL
scheme (e.g. libreoffice:) pointing at a small PowerShell
helper that strips the scheme prefix and shells out.
The flow¶
flowchart LR
B["Browser<br/>user clicks<br/>libreoffice:https://..."]
R["Windows Registry<br/>HKCU\Software\Classes\libreoffice<br/>shell\open\command"]
H["PowerShell helper<br/>1. strip libreoffice: prefix<br/>2. Start-Process soffice.exe"]
O["LibreOffice<br/>opens the document"]
B --> R --> H -->|soffice.exe <url>| O
Step 1 — Write the PowerShell helper¶
Pick a permanent home for the script, e.g.
C:\Tools\LibreOfficeUrlHandler\open.ps1. Save the following:
param([Parameter(Mandatory)][string]$Url)
# Strip the custom-scheme prefix. The URL arrives as
# "libreoffice:https://host/path"; we want "https://host/path".
$clean = $Url -replace '^libreoffice:', ''
# Spawn soffice.exe with the cleaned URL. Start-Process foregrounds
# the new window by default, so an already-running LibreOffice is
# brought to front and the new document becomes the active one.
Start-Process `
-FilePath 'C:\Program Files\LibreOffice\program\soffice.exe' `
-ArgumentList $clean
Notes:
- If LibreOffice is installed as 32-bit on a 64-bit Windows, the
path is
C:\Program Files (x86)\LibreOffice\program\soffice.exeinstead. Adjust-FilePathto match. Start-Processhandles process focus correctly out of the box — no polling / delay dance is needed (unlike the macOS recipe).
Step 2 — Write the registry-import file¶
Create register.reg somewhere temporary (e.g. on the desktop):
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\libreoffice]
@="URL:LibreOffice URL Handler"
"URL Protocol"=""
[HKEY_CURRENT_USER\Software\Classes\libreoffice\DefaultIcon]
@="C:\\Program Files\\LibreOffice\\program\\soffice.exe,0"
[HKEY_CURRENT_USER\Software\Classes\libreoffice\shell\open\command]
@="powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File \"C:\\Tools\\LibreOfficeUrlHandler\\open.ps1\" \"%1\""
Two registry keys are the bare minimum:
- The
URL Protocolvalue (an empty string, but the value must exist) — this tells Windows the key represents a URL handler rather than a file association. shell\open\command\(Default)— the command line Windows runs when something dispatches the scheme.%1is substituted with the full URL the browser handed off (so it arrives in the helper aslibreoffice:https://host/path).
Optional but recommended:
DefaultIcon— gives the scheme a visible icon in Windows surfaces that render it (rare but cheap to add).HKEY_CURRENT_USERis a per-user scope; no admin needed. If you want the handler available system-wide, writeHKEY_LOCAL_MACHINE\Software\Classes\libreofficeinstead — requires elevation.
A few escaping details that bite if missed:
- Backslashes in registry string values must be doubled:
C:\\Tools\\…\\open.ps1. - Double quotes inside the value must be escaped with a
backslash:
\"%1\". - Wrap
%1in\"…\"— without it, URLs containing whitespace (rare but possible) get split into multiple arguments.
Step 3 — Install the scheme¶
Double-click register.reg. Windows prompts:
Are you sure you want to continue?
Click Yes. The keys are merged into the registry instantly — no reboot, no logout. The scheme is now live for the current user.
To verify the registration:
Get-ItemProperty 'HKCU:\Software\Classes\libreoffice\shell\open\command'
Should return the PowerShell command line you set in Step 2.
To uninstall later, run from PowerShell:
Remove-Item -Path 'HKCU:\Software\Classes\libreoffice' -Recurse
Step 4 — Generate the link from your application¶
Wrap the actual remote URL with your custom scheme prefix when rendering the "Open in LibreOffice" button:
$remote_url = 'https://example.test/path/to/file.odt';
$lo_url = 'libreoffice:' . $remote_url;
$build['button'] = [
'#type' => 'link',
'#title' => $this->t('Open in LibreOffice'),
'#url' => \Drupal\Core\Url::fromUri($lo_url),
];
When the user clicks the link, the browser hands libreoffice:…
off to Windows, which looks up the registry entry, runs the
PowerShell helper, which shells out to soffice.exe with the
unwrapped https://… URL.
Step 5 — Test¶
- Visit a page that renders the "Open in LibreOffice" button.
- Click it. The first time, the browser prompts "Open libreoffice?" or "Allow this site to open libreoffice links?" — tick Always allow if you want to skip the prompt on subsequent clicks, then click Open.
- LibreOffice should foreground (or launch if not running) with the remote document open.
Troubleshooting¶
The browser does nothing when I click the link¶
- Re-verify the registration with the
Get-ItemPropertycommand above. If the key doesn't exist, the.regimport didn't apply — re-run as the right user. - Some browsers cache a denied permission for unknown schemes. Reset the site's notification / external-app permission via the browser's site-settings panel and try again.
PowerShell complains about execution policy¶
The default Windows execution policy can block ad-hoc .ps1
files. The -ExecutionPolicy Bypass flag in the registry command
line covers most cases, but if your environment forces
AllSigned, you have two options:
- Replace the PowerShell helper with a
.cmdbatch file (no execution policy applies) and update the registry to point at it:
@echo off
set url=%~1
set url=%url:libreoffice:=%
start "" "C:\Program Files\LibreOffice\program\soffice.exe" "%url%"
Registry line becomes:
@="\"C:\\Tools\\LibreOfficeUrlHandler\\open.cmd\" \"%1\""
- Or sign the PowerShell script with an internal code-signing
certificate and switch the registry back to
-ExecutionPolicy AllSigned.
A blank PowerShell window flashes briefly¶
The -WindowStyle Hidden flag in the registry command line
suppresses the PowerShell window. If you skipped that flag,
re-import the .reg with it added.
soffice.exe runs but doesn't pick up credentials in the URL¶
LibreOffice does not parse https://user:pw@host/file.odt
from the CLI on Windows either — same limitation as macOS.
Embed credentials in the URL path instead, e.g.
/<uid>-<hmac>/file.odt, and have your server unpack them
server-side. The browser → PowerShell → soffice.exe chain
passes the path verbatim, so the auth segment survives.
LO complains about an untrusted certificate¶
LibreOffice has its own NSS trust store on Windows too, a
copy of Mozilla's NSS bundled with LO and not the Windows
certificate store. Importing your CA into "Trusted Root
Certification Authorities" via certmgr.msc does not reach
LibreOffice's trust list.
To trust your local CA from LO:
- Tools → Options → LibreOffice → Security → Certificate, click Certificate Path….
- Point LO at a Mozilla NSS folder that already trusts your CA
(typically a Firefox profile directory under
%APPDATA%\Mozilla\Firefox\Profiles\<random>.default-release\). - Or set the
MOZILLA_CERTIFICATE_FOLDERenvironment variable insoffice.exe's startup environment.
soffice.exe runs but the file save fails with 403 (WebDAV-specific)¶
If you're serving the file over WebDAV, two failure modes are worth checking on the server side:
- Query-string auth tokens are stripped by LO before
PUT— this is RFC-compliant (query strings aren't part of the resource identifier). Put any auth token in the URL path instead. The auth survives every verb that way. - Lock tokens are URL-keyed. If your server rewrites the URL
between
LOCKandPUT, the lock token from the LOCK response won't match the resource path onPUTand the server returns 423/412/409. Keep the URL identical across the WebDAV session.