Version 1.49.0

Released: 2015-10-17 new

Custom script:


to be called before DA runs anything from:


All GET/POST variables are passed via environment.

path will also be set accordingly, and api=0|1 if you need to figure out if it was CMD_API or CMD_ that was called.

Saves needing to use for filemanager related blocks.


There is no, because DA will be chrooted, and cannot see scripts once chrooted to /home/user.

Password protected directory to add ErrorDocument 401 new

When password protection is enabled on a Directory in the File Manger, DA will now also add:

ErrorDocument 401 "Unauthorized Access"

if no other:

ErrorDocument 401

lines exist.

When protection is disabled, DA will not remove the line, in case something else was specified.

Note DA expects a space after ErrorDocument, so if a tab is used before the 401, DA won't see the entry and duplicate could be added.

Remote ftp backup/restore to support secure FTPS (SKINS)(LANG) new

Remote ftp backups should be using a secure connection.

Add "Secure TLS" option to the skins.

Changes to the ftp_upload.php will be needed, and likely other scripts too.

Will use a env variable to tell the script to use a secure connection.

Custom scripts that currently exist should be updated to make this work, but not updating shouldn't break anything.


MAKE SURE you test the feature before setting it / leaving it.

Not all remote ftp servers support ftps, so run a test on 1 User first, and confirm the file shows up on the other end in the correct path.


MAKE SURE you have a current version of curl in /usr/local/bin/curl.

The scripts will try /usr/bin/curl as a fallback, but those are usually older and don't have the newer required ssl settings.

If unsure, update with CustomBuild:

cd /usr/local/directadmin/custombuild
./build update
./build set curl yes
./build curl






new environmental variable passed to the scripts:


if ftp_secure==ftps, the upload/list use curl for the tls ftp functions.

The ftp_download still uses php, but just swaps to use ftp_ssl_connect()




2 places under "step 3: where" AND form action=update_files

<tr><td class=list align=right>|LANG_SECURE_FTP|:</td><td class=list>|FTP_SECURE_SELECT|</td></tr>

action=restore form:

<input type=hidden name=ftp_secure value="|ftp_secure|">



<tr><td class=list align=right>|LANG_SECURE_FTP|:</td><td class=list>|FTP_SECURE_SELECT|</td></tr>





Server time on login page new

The bottom of the login page for DA will now show the server's time (using the ctime function)

This will be useful to debug cases where the license shows as expired or not active yet, but also to help debug session cookie expiry issues.

Javascript has also been added to the this which checks the server's time() in seconds to the client's value.

If the difference is greater than 24 hours (session_cookie_multiplier * 60 * session_minutes), then a note will appear in bold red at the bottom of the page, for example:

Computer or server time out of sync by 36.5 hours.

For anyone with custom login pages, their login.html now has these extra tokens:

TIME=Sat Aug 8 10:19:56 2015


optional for customized, only if you want this functionality with your custom login page:


<div id=footer>|TIME|</footer>
<script language="JavaScript">
        computer_time=Math.floor(; server_time=|TIME_SECONDS|;
        if (time_diff>|ALLOWED_OFFSET|)
                document.getElementById('outofsync').innerHTML='Computer or server time out of sync by '+(Math.round(100*time_diff/3600)/100)+' hours.';

When SNI is enabled, Admin SSL certificates are saved to the domain, not to the shared server cert/key new

Previously, any Admin account on the server IP would have their domain's cert/key saved to the shared server certificate/key files at:


However, with SNI, this is not required because openssl and apache can figure out which VirtualHost to use for the certificate.

With this change, when enable_ssl_sni=1 is set in the directadmin.conf, any certificate/key pair saved to an Admin account on the server IP, will have their cert/key pair saved to:

/usr/local/directadmin/data/users/username/domains/ and

as with any other owned IP domain.

This also means that changing the ssl certificates for the hostname or shared IPs (direct IP access: would require manual saving of the certificate to the shared cert location.. but wouldn't be accesed much with SNI, so isn't terribly useful anyway.

Custom Domain Items (SKINS)(LANG) new

Custom Domain Items, similar to the Custom Package Items:

Ability to add your own package items (SKINS)

Create the optional file to enable the feature:


The format is the same as custom_package_items.conf for type=checkbox|listbox|text

But with two added optional values:


where if you set this to "yes", it will show the value to the User, but will not be a form items.

This prevents the User from changing the value by accident, so you can control it with CMD_DOMAIN or CMD_API_DOMAIN.

Keep in mind that it does not mean that the User cannot edit it (they can if they really want to), but it's a little more difficult.

It basically just prevents accidental errors by Users if you don't want the value to be changed.


if hidden=yes, then the CUSTOM_ITEM* tokens will not be filled for that item, but you can still set/read them normally with the API.

This would be useful if you're saving particular data that isn't visiably User friendly, or you don't want the User to know.

Again, they can edit/set it if they want to using the API, but out of sight, out of mind.

For both api_only=yes and/or hidden=yes, you must use CMD_API_DOMAIN.

DA will ignore the items set to api_only=yes or hidden=yes for CMD_DOMAIN calls, to account for the checkbox item that would get shut off anytime the User made a change.

Keep in mind that any skin that is not updated with these changes means that if a User saves a domain setting, the checkbox value would be read as OFF, since it's not passed.

For the other values, they would use the default values, so be sure to set the defaults if you want them set.

DA will not add the values to the when the domain is created, so treat any missing variables as using your default value.

Once you or the User saves a change, all variables will be set.

To view your values with the API, use CMD_API_ADDITIONAL_DOMAIN with action=view:


To set the variables with the API, use CMD_API_DOMAINS, action=modify.

*** Make sure to pass all variables, not just the ones you want to set. ***

For example, if you don't include php=ON, then you'll end up turning off PHP when you save your custom item.

So you'd need to first read the settings with CMD_API_ADDITIONAL_DOMAIN action=view so you know what to return to DA.

DA does some very basic User sanitation, but use if you want to make sure the values passed are what you're expecting.

Abort with a non-zero exit code and echo some text.

And use for any triggers you need, the variables should be passed via environment to both the pre/ scripts.




add extra tokens for custom items.

Also changed the table to be 3 columns wide, rather than 2.

<tr><td class=list2>|CUSTOM_ITEM_1_STR|</td><td class=list2 align=center>|CUSTOM_ITEM_1|</td><td class=list2>|CUSTOM_ITEM_1_DESC|</td></tr>
<tr><td class=list>|CUSTOM_ITEM_2_STR|</td><td class=list align=center>|CUSTOM_ITEM_2|</td><td class=list>|CUSTOM_ITEM_2_DESC|</td></tr>

etc... we included 5 entries, but the custom_domain_items.conf does allow up to 99.

If you actually want to do it dynamically, consider using the |$/usr/local/bin/php method to generate each tr on the fly in a loop, rather than having to check for each one manually.





HSTS header: HTTP Strict Transport Security new

New option for HTTP Strict-Transport-Security in the directadmin.conf, internal default is:


which is disabled. Value will be in seconds.



is set in the directadmin.conf and hsts is greater or equal to 0, then the header will be set to that value:

Strict-Transport-Security: max-age=5184000

NOTE it will only be added to the login page, and not any other page.

DA's debug level 2500 will let you know if it's added.

See "IMPORTANT" below.

To disable the header, you must set it to -1 in the directadmin.conf or delete the hsts value from the directadmin.conf, reverting to the internal -1 default:


Because browsers will remember the setting, if you are going from a large value (5184000), to make the browser "forget", you must set it to 0 for a while:


so that the header is sent to clients, and set to 0, shutting it off.

After all browsers/client have received the change, then you can set it to -1.

If SSL=0, this header is not added, and will not apply.


when this feature is used on DA's port (2222), browsers have been noticed to carry over the behavior onto the apache ports (443).

Meaning, if you do the following:

it will set the header, so far so good.

So if you go to apache on port 80 (should be unlreated)

this will redirect you to (apache 443), as per how the feature works, even though it's not what we want.

I'm not currently aware to tell HSTS to only do http://2222 -> https://2222 and ingore 80/443, which is an unrelated service.

This is why the feature is diabled (-1) by default.

If you use it, I'd recommend using:

on a host value ( that you don't use for apache (, or else it will affect your connect to your website if you've previously visited DA.

If you use HSTS in apache as well, then you should be fine.

If you don't force the host value, then if the client uses their own domain, like:

then the header will then apply to, forcing them to if they previously accessed DA.

Mail Queue Admin to use -bpr instead of -bp for faster output new

Previously, exim -bp was used to get the full list of emails, but we've changed it to use -bpr, which tells exim not to sort it before output, since our table class sorts before being displayed anyway.

Option to allow Resellers to reset their User's daily E-Mail send count new

Related to this Admin feature:

Ability to reset today's email usage count to allow more sends

new directadmin.conf option, default:


where 0 is the internal default (disabled).

If you set it to 1, then all Resellers will have the ability to reset their User's send count to 0.

You'd usually use this option with it:


as they could alternatively just increase the limit anway, so might as well let them reset the limit.

Ability to pass custom variables to pre/ scripts from GET/POST new

There are many pre/ scripts in DA, used as hooks for almost any action.

However, sometimes, you might want to give some custom variable to these scripts via GET or POST, without having to fight with the custom package items or other convoluted means.

The solution to this is to use an set of prefixed variables, that are exclusive to the scripts.

New directadmin.conf value:


Internal default value is 0, which is disabled.

To enable the feature, add it to your directadmin.conf, and set it to 1.

You can use any GET/POST variable name you want from these characters:


underscores and dashes are allowed. No spaces.

but it must start with the prefix:


So, a sample variable, passed with GET or POST might be:


which would let you access:


in any hook script that is called with that request.

Note the maxium length of an environmental value is 125749 bytes.

Anything greater to or equal to that length will be ignored, and it's env variable will be unset if it was present already. new

New custom script:


Very similar to

however, the is called for every request, and checks the password for each one.

The only applies to DA, just before it creates the session file, but after the password has been validated.

Note that API calls DO NOT use session files, so this should not be a login filter.

This script can be handy if you'd like to notify yourself of a session based login, but you won't be notified for any API based actions for the same account (this won't be called for API calls)

The variables passed will be the same as the, so check id=1223.

except I've added a new env variable:


to both the and

Be sure to exit with a zero-status.

If you exit with a non-zero status, the write of the session file will be aborted, and the login won't work.

All non-zero exit statuses will be logged to the /var/log/directadmin/error.log, but the login page will just refresh with no errors.

Ability to override skin html on a per-user basis new

You can now add an encoding override to a User by adding:


to their user.conf.

This will override the LANG_ENCODING token set in the skin.

It would prevent the need for this tool:

but only on a per-user basis.

Autoreply and Vacation messages to support html or utf-8 (SKINS) new

SpamBlocker 4.4.x will now support header custom headers for auto-reply messages and vacation messages.

Manual update to SpamBlocker 4.4.x required to make use of this.

Only added to "enhanced" skin, not to power_user or default.

DA will save header info to:


and if that file exists, the exim.conf will add those headers to the message.

DA checks the /etc/exim.conf version by looking at the top 5 lines, so don't remove the version from the top.

We don't recommend anything else other than the version info as the top line, as CB2 only checks the top line.


add "reset once file" button.





which is a duplicate of:


just so we can add dynamic content.

Both will point to the same file, but the HTM_ version won't support this feature, so change your URLs that point to it.

Doesn't apply to the actual POST of the creation.. only to the display of the form used to enter the data.

HTM_ path in files_user.conf can remain for backwards compatibility, just copy the line and change HTM_ to CMD_ (same actual .html file)

CMD_API_EMAIL_AUTORESPONDER_CREATE has also been created if you want to get the info that the page receives (encoding and content-type options)

Also added new file:


used for inclusion.

contains the options for form headers:










Creating an autoresponder with the extended information.


method: POST
action = create
domain =
user = fred
subject = Autoreply
reply_encoding = UTF-8
reply_content_type = text/plain
reply_once_time = 2d
text = I'll get back to you!
cc = ON
email =
create = Create

Per-User mysql.conf files new

Previously, there could only be 1 database server, as specified in:


This feature allows you to specify some other mysql.conf file, on a per-User basis, so some can be on DB server A, and others on B, or as many as you want to specify (only one server per User though)

To use this feature, you must first turn it on by adding:


to your directadmin.conf, and restarting directadmin.

The internal default is 0.

Once enabled, the database class in DA will then read in the user.conf for the given User.

To override the default mysql.conf, you'd simply add:


in the:


for that account, and DA will start using it for everything, in place of the mysql.conf.

The path you set can be anything, but the read of the file only has "diradmin" access, so for simplicity, you might want to keep it in the same path, same permissions, like the mysql.conf.

The "othermysql.conf" has 100% the same functionality as the mysql.conf, so you can specify different mysql.sock files, or different host or access_host values.

Also, because mysqldump and mysql restores make use of:


any action that typically rebuilds that file, will now rebuild one for each User that has a customized mysql.conf, eg:


so that there are no conflict with running backups at the same time using different values.

SKIN token value:


if feature is disabled, or not set in the user.conf, value will be blank, eg ""

directadmin.conf variable: systemd new

Debian 8 and CentOS 7 typically both use systemd/systemctl by default, so DirectAdmin as coded with hard #ifdef checks based on the OS the binaries were compiled on.

Some instances of Ubuntu based on Debian 8 do not use systemd, so this causes several issues, including DA's inability to restart services, and the mysql.sock path, among other things.

New directadmin.conf variable:


The default value depends on which OS you're running.

The value affects the following variables, systemd=1 above, systemd=0 below:




/bin/systemctl or /usr/bin/systemctl


/sbin/service or /etc/init.d/ or /usr/local/etc/rc.d/

apache syntax check with /usr/sbin/httpd -t

service/init.d httpd configtext








Ability to set global/user/domain/subdomain custom Apache/Nginx tokens new

The existing virtual_host2*.conf templates allow you to do pretty much anything you need, but there are cases where you might only want to add or change one small variable without creating an entire new set of templates.

This feature allows you to insert your own global tokens into a config file:


set with the internal directadmin.conf default:


one token per line, with a = character, followed by the value you want to set.

There will also be similar files for users, domains and subdomains, if you create them:


load in that order.

They will be loaded after the DA based tokens are set, but before the CUSTOM tokens, so that they're available in the files.

If you want to set a CUSTOM token, do that in the

This type of setup can be handy in the event that you may include a set of templates for many servers, and need to update those templates from time to time, but you might want to have a different tokens in each server, used by those templates (or per-domain customization)

Sent E-Mails on "Show All Users" new

NOTE: column will be blank until the show_all_users.cache cache is rebuilt, usually after the next tally has completed.

To help Admins better track the amount of outbound emails for a given User, I've added a new column to the page:

Admin Level -> Show All Users


Sent E-Mails

referring to the number of E-Mails that have left the server by this User account.

It will show 2 numbers, eg:

110 (Today: 10)

Which would mean that 100 emails have been sent on previous days of the month, and 10 emails have been sent today.

The "100" number is stored in the show_all_users.cache file, but the "today 10" is loaded on the fly, so this could slow things down somewhat.

But rather than looking for the usage file for each User which would slow it down with May Users (/etc/virtual/usage),

DA instead reads in all files from /etc/virtual/usage/* which will usually be a much smaller subset, vs the total number of Users, and will save this list in memory, so as the table is filled, it only adds the (Today:xx) number to the total if the file existed.

If no usage is visible "today", then it would instead show just:


Allow incoming server IP for local session keys new

Previously, session keys were only valid from, or if allow_foreign_key=1 was set.

In some cases, even when curl_setopt(CURLOPT_INTERFACE, ''); is explicitly set, the connection still arrived to DA from the server IP.

The change is to allow session keys from the server IP, as obtained from the ethernet_dev setting in the directadmin.conf, using ioctl(sck, SIOCGIFADDR, &ifr);

CNAME values to be allowed underscores new

Bug with the allow_dns_underscore=1 where the CNAME value didn't get allowed as the "VALUE" is checked for syntax for resolvability, part of which is a "valid domain" syntax check, which didn't allow underscores. Changed the check to allow underscores, but allow_dns_underscore=1 is still required.

This change only applies to a CNAME value. The "name" (left side) would have already allowed for underscores.

Removed nginx SSL settings, replaced with global settings (TEMPLATES) new






        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout  5m;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers   on;

From the above templates.

The following has been added to /etc/nginx/nginx-defaults.conf:

ssl_session_cache   shared:SSL:10m;
ssl_session_timeout  5m;

You may need to type:

cd /usr/local/directadmin/custombuild
./build update
./build rewrite_confs

in order to get the new nginx-defaults.conf file.

If not, ssl_session_cache will default to "none",

and ssl_session_timeout will default to 5m... which is what's set anyway.

Lost password path to use lang encoding (LANG) fixed

The results for the CMD_LOST_PASSWORD are not being displayed with the correct encoding setting.


internal/authentication.txt #11

All result pages from the lost password will now include a <head><meta> and in it, DA will set an encoding as specified by:

internal/authentication.txt #64

which will default to UTF-8.

The reason it was set in that file, is that all other related strings are in that file, making it easier to keep track of.

dns_spf restore causes duplicate SPF fixed

When SPF records are enabled with:


User migrations cause both the backup SPF and new server SPF records to be present.

Related to a similar change for TXT records:

spf record IP not swapped with restores

DA will now always drop the old SPF records, and use the default SPF records for the new server.

A checkbox feature is in the versions system to allow the Admin to decide to use either the backup SPF or local SPF records (same for TXT)

Checkbox to restore SPF TXT from backup, or use local value (SKINS)

but is not yet implemented.

Forum thread:

Security: open_baseidr on /~username with CLI fixed


To set the fix for all Users after you update DA, run:

cd /usr/local/directadmin/custombuild

./build rewrite_confs

For the user_virtual_host.conf template, open_basedir (OBD) was not set for ~username access because this setting (Admin Level -> Php Config) is a per-domain setting, and the /~username method is a per-user setting, so the template didn't have a domain config to check.

The fix was to make additions to the user_virtual_host.conf to add some default changes, making their settings use the default values, as per the Admin Level -> Php Config global default for new Users.

So if you have OBD turned "On" as the default for new Users, all Users will get this setting for their /~username access method, regardless of if it's shut off for their domain.

If you need override this and shut it off only for 1 User, then edit:


and add:




to override the global php config OBD default setting for /~username access.

The override shouldn't affect the per-domain settings for OBD.

New user_virtual_host.conf token:


if OBD is on and CLI is available.

Also added the CLI_PHP_MAIL_LOG setting into the template, so that mail from /~username is logged to /home/user/.php/php-mail.log, like it is for domains.

This check uses the USER_CLI token and not CLI token, because we don't want to cause a conflict with the per-domain settings, which may be turned off.

Changes to how DA figures out which server to use for updates fixed

Internal changes with how DA determine which IPs to use.

It will compare both a hard-coded list as well as a dynamic list using DNS.

For now, this changes will only affect the "Current Available Version", as displayed in the licenses/updates area, and the download of license.key files.

This is to ensure it's working correctly, in case it dose have bug and clients can still update DA itself to a fixed version.

This will be extensively tested, but a stepped approach to it's implementation is still the safer route.

group not correctly set fixed

In a few places when setting the process to an effective group, the group was set after the unprivileged effective user was set, so the group couldn't get set.

Fix was to ensure the group is set first (while euid=0), then set the user after.

Ftp backups created in /home/tmp to include pid in path fixed

Previously, backup files uploaded with ftp would be assembled in temporary locations, eg:


The issue with that would be if you had created multiple backup crons that were running at the same time, the start of the 2nd would clean it's work area (/home/tmp/admin) before starting, likely wiping out all data of the other backup cron.

Because FTP backups can be to different locations, this should be allowed, so the fix is to make the path:


where .1234 will be added to the path, where 1234 is the PID of the current dataskq process.

IMPORTANT: if you have any custom scripts in:


that relate to backups or filenames, be sure to update them to account for the PID in the backup creator's name.

If you don't want to touch your scripts, you can shut off this feature to back to the old way of the conflicting paths.

The default internal directadmin.conf setting will be:


so if you want to disable the functionality, set:



Local backups will not receive this behavior for two reasons:

  1. The path will not be cleaned, so we don't want /home/admin/admin_backups.1234 for each created backup, it should overwrite the previous files.

  2. If you have two backups running at the same time, writing to the same file, you might want to re-asses your cron schedule.

Skip all templates in ~/domains/ upon restore creation fixed

If a User does not exist, previously a restore would first have DA create the User/Domain with the default paths.

After creation, the tar.gz would be extracted overtop, setting the data.

However, sometimes people might have links, eg:

/home/user/domains/ -> /some/other/path

Fix is to extend a previous change:

rename index.html to index.html.moved prior to restore of user backup

where a skeleton/template is not processed for a domain on restore, so the template is skipped.

This skip has now been extended to all folders to the ~/domains/ folder (excluding public_ftp).

So the new functionality is:

If a Domain does exist during a restore, it will be created prior to extracting the tar.gz.

But the creation processes will not create anything in ~/domains/ (except public_ftp)

and assumes that the extraction of the tar.gz will set the data as it was.

Allow login-as master API access to User if master has api_with_password=yes fixed

Relating to the api_with_password user.conf option, when using the "Login As" method to login to a User account, when accessing an API, should the User have api_with_password=no set, this fix will then query that setting for the master that is doing the "Login As" on the API command. If it's denied for the User, the master must have it enabled to be able to access the API. Previously, the master wouldn't be able to access it, regardless of their setting, when the User is set to "no".

Note: this does not refer to Login Keys or Session Keys.

If you're running a script to call the DA API, you really should be using a Login Key anyway, which are always allowed regardless of the api_with_password setting.

Last Updated: