This article will introduce you to the key elements to check regarding security and privacy.

Privacy configuration and hardening

Click here


Access management

Click here


Server configuration

Click here


Scripting and coding guidelines

Click here


Network, database and SSL/TLS

Click here


Web-server configuration

Click here



Privacy configuration and hardening is a key element of security optimization. Read out this section to learn more on privacy best practices:

  • Protect your customer PII by using HTTPS instead of HTTP
  • Use PII view restriction to protect privacy and prevent data from being misused.
  • Make sure that encrypted passwords are restricted.
  • Protect the pages that might contain personal information such as mirror pages, web applications, etc.

Privacy Requests

Adobe Campaign offers a set of tools to help you with your Privacy Compliance for GDPR and CCPA.

Refer to this page for general information on what Privacy Management is and the implementation steps in Adobe Campaign. You will also find best practices and an overview of the user process and personas.  

URL Personalization

When adding personalized links to your content, always avoid having any personalization in the hostname part of the URL to avoid potential security gaps. The following examples should never be used in all URL attributes <a href=""> or <img src="">:

  • <%= url >
  • https://<%= url >
  • https://<%= domain >/path
  • https://<%= sub-domain >.domain.tld/path
  • https://sub.domain<%= main domain %>/path


To validate and ensure that you are not using above, run a query on tracking URL table via Campaign Generic Query Editor or create a workflow with filter criteria in the query activity.


  1. Create a workflow and add a Query activity. Learn more.
  2. Open the Query activity and create a filter on the nmsTrackingUrl table as follows: source URL starts with http://<% or source URL starts with https://<%.
  3. Run the workflow and check if there are result.
  4. If so, open the output transition to view the list of URLs.
Query on Dynamic URLs

Signature mechanism

To improve security, a new signature mechanism for tracking links in emails has been introduced in Build 19.1.4 (9032@3a9dc9c), and is available in Build 19.1.4 (9032@3a9dc9c) and Campaign 20.2. This option is enabled by default for all customers.


When a malformed signed URL is clicked, it will return the following error: "Requested URL '… ' was not found."

In addition, hosted and hybrid customers on Build 19.1.4 (9032@3a9dc9c and 9032@800be2e) and on Campaign 20.2 can use an enhancement to disable URLs generated from previous builds. This option is disabled by default. You can reach out to Customer Care to enable this feature.

To activate this new mechanism, on-premise customers need to follow these steps on all Campaign servers:

  1. In the server configuration file (serverConf.xml), change blockRedirectForUnsignedTrackingLink to true.
  2. Restart the nlserver service.
  3. On the  tracking server, restart the web server (apache2 on Debian; httpd on CentOS/RedHat; IIS on Windows).


Customers running on Build 19.1.4 (9032@3a9dc9c) can experience issues with push notification deliveries using tracking link, or deliveries using anchor tags. If so,  Adobe recommends to disable the new signature mechanism for tracking links. 

Hosted and hybrid customers must reach out to Customer Care to have this mechanism disabled.

On-premise customers can follow the step below :

  1. In the server configuration file (serverConf.xml), change signEmailLinks to false.
  2. Restart the nlserver service.
  3. On the  tracking server, restart the web server (apache2 on Debian; httpd on CentOS/RedHat; IIS on Windows).

Data restriction

You have to make sure that the encrypted passwords will not be accessible by a low privilege authenticated user. To do that, there are two main way: restrict access to password fields only or to the entire entity (need a build >= 8770).

This restriction allows you to remove passwords fields but let the external account accessible from the interface for all users. Refer to the documentation.

  1. Go in Administration > Configuration > Data schemas.

  2. Create a new Extension of a schema.

  3. Choose External Account (extAccount).

  4. In the last wizard screen, you can edit your new srcSchema to restrict access to all password fields:

    You can replace the main element (<element name="extAccount" ... >) by:

    <element name="extAccount">
        <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="password"/>
        <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="clientSecret"/>
        <element name="s3Account">
          <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="awsSecret"/>
        <element name="wapPush">
          <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="password"/>
          <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="clientSecret"/>
        <element name="mms">
          <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="password"/>
          <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="clientSecret"/>

    So your extended srcSchema can look like:

    <srcSchema _cs="External Accounts (cus)" created="2017-05-12 07:53:49.691Z" createdBy-id="0"
               desc="Definition of external accounts (Email, SMS...) used by the modules"
               entitySchema="xtk:srcSchema" extendedSchema="nms:extAccount" img="" label="External Accounts"
               labelSingular="External account" lastModified="2017-05-12 08:33:49.365Z"
               mappingType="sql" md5="E9BB0CD6A4375F500027C86EA854E101" modifiedBy-id="0"
               name="extAccount" namespace="cus" xtkschema="xtk:srcSchema">
      <createdBy _cs="Administrator (admin)"/>
      <modifiedBy _cs="Administrator (admin)"/>
      <element name="extAccount">
        <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="password"/>
        <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="clientSecret"/>
        <element name="s3Account">
          <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="awsSecret"/>
        <element name="wapPush">
          <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="password"/>
          <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="clientSecret"/>
        <element name="mms">
          <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="password"/>
          <attribute accessibleIf="$(loginId) = 0 or $(login) = 'admin'" name="clientSecret"/>


    You can remplace $(loginId) = 0 or $(login) = 'admin' by hasNamedRight('admin') to let all users with admin right see these passwords.

Protecting pages containing PII

We strongly advise on-premise customers to protect the pages that might contain personal information such as mirror pages, web applications, etc.

The goal of this procedure is to prevent these pages from being indexed, thus avoiding a potential security risk. Here are a few useful articles:

To protect your pages, follow these steps:

  1. Add a robots.txt file at the root of your web server (Apache or IIS). Here is the content of the file:

    # Make changes for all web spiders
    *Disallow: /

    For IIS, refer to this page.

    For Apache, you can place the file in /var/www/robots.txt (Debian).

  2. Sometimes adding a robots.txt file is not sufficient in terms of security. For example, if another website contains a link to your page, it might appear in a search result.

    In addition to the robots.txt file, it is advised to add a X-Robots-Tag header. You can do it in Apache or IIS and in the serverConf.xml configuration file.

    For more information, refer to this article.

Access Management


Access management is an important part of security hardening. Here are some of the main best practices:

  • Create enough security groups
  • Check that each operator has the appropriate access rights
  • Avoid using the admin operator and avoid having too many operators in the admin group

Refer to the documentation: Access rights and Folder access properties.

Webapp operator

Out of the box, the webApp operator is an administrator. To improve security, follow these guidelines:

  • Replace the admin named right from this operator with a new one (can be named 'webapp'). For more information, refer to this documentation.
  • Add the webApp operator in folders (mainly recipient folders) to grant read/write access to recipients. For more on this, refer to this documentation.
  • If using a multi-brand (or multi-geo) instance, you may want to split web application access to different recipient folders. To do so:
    1. Duplicate the webApp operator
    2. Enter a name for each duplicate. For example: webapp_brand, webapp_brand2, etc.
    3. Duplicate a web application template to have one template per brand and edit the properties to change the operator by selecting Use a specific account.  Learn more in this documentation.

Security groups and admin operators

Create enough security groups to give just enough rights to your operators to let them do what they need, and not more.

Do not use the admin operator (or don't share it). Create one operator per physical user (to have an accurate audit/logging). Add your newly named administrators to the admin group. If you don't use the admin operator, do not delete it, and don't disable it: this operator is used internally to execute processing. But you can ban its access to the client console and restrict its security zone (to localhost).

Avoid adding too many operators in the admin group (or with admin named rights). They are very powerful operators (they can perform all SQL statements, execute commands on the server, etc.).

Adobe Campaign provides three high-level privileges through named rights:

  • ADMINISTRATION (admin): gives access to everything and allows to do everything, bypassing all named right checks, so it includes the PROGRAM EXECUTION (createProcess) and SQL named rights
  • PROGRAM EXECUTION (createProcess): allows executing external programs (on the server)
  • SQL: allows running SQL scripts on the database (so it can bypass the security model). Note: if you need to perform complex computations (filtering, for example), you can ask your database administrator to create an SQL function and add them to the allow list. Learn more in this section.
  • Grant them to very few (and trusted) operators

Scripting and coding guidelines


When developing in Adobe Campaign (workflows, Javascript, JSSP, etc.), always follow these guidelines:

  • Scripting: try to avoid SQL statements, use parameterized functions instead of string concatenation, avoid SQL injection by adding the SQL functions to use to the allow list.
  • Securing the data model: use named rights to limit operator actions, add system filters (sysFilter)
  • Adding captchas in web applications: learn how to add captchas in your public landing pages and subscription pages.


For more details, refer to Campaign JSAPI documentation.

If you script using workflow, web applications, jssp, follow these best practices:

  • Try to avoid using SQL statements as much as you can.
  • If you need to, use parameterized (prepare statement) functions instead of string concatenation.

Bad practice:

sqlGetInt( "select iRecipientId from NmsRecipient where sEmail ='" + request.getParameter('email') +  "'  limit 1" )

Good practice:

sqlGetInt( "select iRecipientId from NmsRecipient where sEmail = $(sz) limit 1", request.getParameter('email'));

Warning: sqlSelect doesn't support this feature, so you have to use the query function of DBEngine class:

var cnx = application.getConnection() 
var stmt = cnx.query("SELECT sFirstName, sLastName FROM NmsRecipient where sEmail = $(sz)", request.getParameter('email'))
for each(var row in stmt) logInfo(row[0] + " : " + row[1]) 

To avoid SQL injections, SQL functions must be added to the allow list to be used in Adobe Campaign. Once they are added to the allow list, they become visible to your operators in the expression editor. Refer to the documentationWarning: if you are using a build that is older than 8140, the XtkPassUnknownSQLFunctionsToRDBMS option might be set to '1'. If you want to secure your database, delete this option (or set it to '0').

If you are using user input to build filters in queries or SQL statements, you always have to escape them (refer to Campaign JSAPI documentation - Data protection: escaping functions). These functions are:

  • NL.XML.escape(data)
  • NL.SQL.escape(data)
  • NL.JS.escape(data)
  • NL.XML.escapeAttribute(data)

Securing your new data model

Folder based

Refer to these sections of the product documentation:

Named rights

In addition to the folder-based security model, you can use named rights to limit operator actions:

  • You can add some system filters (sysFilter) to prevent reading/writing to your data. See the documentation.
<sysFilter name="writeAccess">     
    <condition enabledIf="hasNamedRight('myNewRole')=false" expr="FALSE"/>   
  • You can also protect some actions (SOAP method) defined in schemas. Just set the access attribute with the corresponding named right as the value.
<method name="grantVIPAccess" access="myNewRole">


For more on this, refer to the this section.


You can use named rights in the command node in a navtree. It gives a better user experience but doesn't provide any protection (use only client side to hide / disable them). You have to use the access attribute.

Overflow table

If you need to protect confidential data (part of a schema) depending on the operator access level, do not hide them in the form definition (enabledIf/visibleIf conditions). The full entity is loaded by the screen, you can also display them in column definition. To do this, you have to create an overflow table. Refer this documentation.

Adding captchas in web applications

It is a good practice to add a captcha in public landing pages/subscription pages. Unfortunately, adding a captcha in DCE (Digital Content Editor) pages is not easy. We will show you how to add a v5 captcha or a Google reCAPTCHA.

The general way to add a captcha in the DCE is to create a personalization block to include it easily within the page content. You will have to add a Script activity and a Test.

Personalization block

  1. Go to Resources > Campaign Management > Personalization blocks and create a new one.

  2. Use the Web application content type and check Visible in the customization menus.

    For more information, refer to the documentation.

    Here is an example of a Campaign captcha:

    var captchaID = CaptchaIDGen();
    <img src="/nms/jsp/captcha.jsp?captchaID=<%=captchaID%>&width=200&height=50&minWordSize=8&maxWordSize=8"/>
    <input id="captchaValue" name="captchaValue" <%= String(ctx.vars.captchaValid) === "false" ? class="ui-state-error" : "" %>>
    <input type="hidden" name="captchaID" value="<%=captchaID%>"/>
    if( serverForm.isInputErroneous("captchaValue") ) {
    <script type="text/javascript">  
    • Lines 1 to 6 generate all needed inputs.
    • Lines 7 to the end handle errors.
    • Line 4 allows you to change captcha gray box size (width/height) and the length of generated word (minWordSize/maxWordSize).
    • Before using Google reCAPTCHA, you must register on Google and create a new reCAPTCHA site.
    <div class="g-recaptcha" data-sitekey="YOUR_SITE_KEY"></div>

    You should be able to disable the validation button, but as we don't have any standard button/link, it's better to do it in the HTML itself. To learn how to do it, refer to this page.

Updating your web application

  1. Access the the properties of your web application to add a boolean variable named captchaValid.

  2. Between the last page and the Storage activity, add a Script and a Test. Plug the branch True to the Storage and the other one to the page which will have the captcha.

    Captcha - step2
  3. Edit the condition of the branch True with "[vars/captchaValid]" equals True.

    Captcha - step3
  4. Then, edit the Script activity. The content will depend on the chosen captcha engine. 

  5. Finally, you can add your personalized block in the page: refer to the documentation

    Captcha - step5
    Captcha - step6

    WARNING: for reCAPTCHA integration, you have to add client-side JavaScript in the HTML (in <head>...</head>):

    <script src="" async defer></script>

Campaign Captcha

var captchaID = request.getParameter("captchaID");
var captchaValue = request.getParameter("captchaValue");
if( !CaptchaValidate(captchaID, captchaValue) ) {
                           "The characters you typed for the captcha must match the image ones.",
  ctx.vars.captchaValid = false
  ctx.vars.captchaValid = true

Line 6: you can put any kind of error message.

Google reCAPTCHA

Please refer to the official documentation.

ctx.vars.captchaValid = false
var gReCaptchaResponse = request.getParameter("g-recaptcha-response");
// Call reCaptcha API to validate it
var req = new HttpClientRequest("")
req.method = "POST"
req.header["Content-Type"] = "application/x-www-form-urlencoded"
req.body = "secret=YOUR_SECRET_HERE&response=" + encodeURIComponent(gReCaptchaResponse)
var response = req.response
if( response.code == 200 ) {
  captchaRes = JSON.parse(response.body.toString(response.codePage));
  ctx.vars.captchaValid = captchaRes.success
if( ctx.vars.captchaValid == false ) {
                           "Please validate the captcha",
  logInfo("reCaptcha not validated")

To use JSON.parse you have to include "shared/json2.js" in your webApp:

Captcha - step7

Since build 8797, in order to use the verification API URL, you have to add it to the allow list in the serverConf file by adding in urlPermission node:

<url dnsSuffix="" urlRegEx=""/>

Network, database and SSL/TLS


A very important thing to check when deploying an on-premise type of architecture is the networking configuration. Ensure that the Tomcat server is NOT directly accessible outside the server:

  • Close the Tomcat port (8080) on external IPs (must work on localhost)
  • Do not map the standard HTTP port (80) to the Tomcat one (8080)

When it's possible, use a secure channel: POP3S instead POP3 (or POP3 over TLS).


It is imperative that you follow your database engine security.

SSL/TLS Configuration

To check the certificate, you can use openssl. To check active ciphers, you can use nmap:

# usage: [port]

echo |\
openssl s_client -connect ${REMHOST}:${REMPORT} -servername ${REMHOST} 2>&1 |\
openssl x509 -noout -subject -dates
nmap --script ssl-enum-ciphers -p ${REMPORT} ${REMHOST}

You can also use an sslyze python script which does both.

python --sslv2 --sslv3 --tlsv1 --reneg --resum --certinfo=basic --hide_rejected_ciphers --sni=SNI

Server configuration


Configuration has to be performed on all servers. The configuration files are of the type serverConf.xml and config-<instance>.xml. Here are the key elements that need to be verified:

  • Security zones: Configure security zones so that they directly take into account the IP addresses of clients of a proxy.
  • File upload protection: limit the types of files that can be uploaded to the Adobe Campaign server using a new uploadAllowList attribute. This can be used in the server configuration file.
  • Relay: fine tune the relay configuration by deactivating the relay rules for unused modules/applications.
  • Outgoing connection protection and Command restriction (server-side)
  • You can also add extra HTTP headers, activate checkIPConsistent, enableTLS, sessionTimeOutSec, etc.

Refer to the Campaign server configuration documentation and the Server configuration file description for more information.

Configuring security zones


From build 8977 onwards, the Security Zones Self Service User Interface is not available anymore.

  • If you are hosted on AWS, adding IP to the allow list must be performed in Control Panel. For more information, refer to the dedicated documentation.
  • If you are not hosted on AWS, reach out to Adobe support team to add IP to the allow list.

 To check if your instance is hosted on AWS, follow the steps detailed in this section.

To learn how to use the Security Zones Self Service UI to manage entries in the VPN Security Zone configuration, refer to this technote.

Make sure that your reverse proxy in not allowed in subNetwork. If it is the case, all traffic will be detected as coming from this local IP, so will be trusted.

Minimize the use of sessionTokenOnly="true":

  • Warning: If this attribute is set to true, the operator can be exposed to a CRSF attack.
  • In addition, the sessionToken cookie is not set with an httpOnly flag, so some client-side javascript code can read it.
  • However Message Center on multiple execution cells needs sessionTokenOnly: create a new security zone with sessionTokenOnly set to "true" and add only the needed IP(s) in this zone.

When possible, set all allowHTTP, showErrors to be false (not for localhost) and check them!

  • allowHTTP = "false": forces operators to use HTTPS
  • showErrors = "false": hides technical errors (including SQL ones). It prevents displaying too much information, but reduces the capability for the marketer to solve mistakes (without asking for more information from an administrator)

Set allowDebug to true only on IPs used by marketing users/administrators who need to create (in fact preview) surveys, webApps and reports. This flag allows these IPs to get relay rules displayed and to debug them.

Never set allowEmptyPassword, allowUserPassword, allowSQLInjection to true. These attributes are only here to allow a smooth migration from v5 and v6.0:

  • allowEmptyPassword lets operators have an empty password. If this is the case for you, notify all your operators to ask them to set a password with a deadline. Once this deadline has passed, change this attribute to false.
  • allowUserPassword lets operators send their credentials as parameters (so they will be logged by apache/IIS/proxy). This feature was used in the past to simplify API usage. You can check in your cookbook (or in the specification) whether some third-party applications use this. If so, you have to notify them to change the way they use our API and as soon as possible remove this feature.
  • allowSQLInjection lets the user perform SQL injections by using an old syntax. As soon as possible carry out the corrections described in the documentation to be able to set this attribute to false.

You can use /nl/jsp/ping.jsp?zones=true to check your security zone configuration. This page displays the active status of security measures (computed with these security flags) for the current IP.

HttpOnly cookie/useSecurityToken: refer to sessionTokenOnly flag.

Minimize IPs added to the allow list: Out of the box, in security zones, we have added the 3 ranges for private networks. It is unlikely that you will use all of these IP addresses. So keep only the ones that you need.

Update webApp/internal operator to be only accessible in localhost.

File upload protection

Check with operational users what kind of files they upload to the server using nlclient/web interface. As a reminder, business needs can be:

  • images (jpg, gif, png, ...)
  • content (zip, html, css, ...)
  • marketing assets (doc, xls, pdf, psd, tiff, ...)
  • email attachment (doc, pdf, ...)
  • ETL (txt, csv, tab, ...)
  • etc.

Add all of them in serverConf/shared/datastore/@uploadAllowlist (valid java regular expression). Learn more in the related documentation

Adobe Campaign does not restrict the file size. But you can do it by configuring IIS/Apache. Learn more in this section.


Please refer to the documentation for more information.

By default, all dynamic pages are automatically relayed to the local Tomcat server of the machine whose Web module is started. You can choose to not relay some of them. If you are not using some Adobe Campaign modules (such as webapp, interaction, some jsp) you can remove them from relay rules.

Out of the box, we have forced the capability to display end user resources using http (httpAllowed="true"). As these pages can display some PII (such as email content, address), redeem coupon, offer, you should force HTTPS again on these paths.

If you are using different host names (one public and one for operators), you can also prevent the relaying of some resources needed by operators over the public DNS name.

Outgoing connection protection

The default list of URLs that can be called by JavaScript codes (workflows, etc.) is limited. To allow a new URL, the administrator needs to reference it in the serverConf.xml file.

Three connection protection modes exist:

  • Blocking : all URLs that do not belong to the allow list are blocked, with an error message. This is the default mode after a postupgrade.
  • Permissive : all URLs that do not belong to the allow list are allowed.
  • Warning : all URLs not on the allow list are allowed, but the JS interpreter emits a warning, so that the administrator can collect them. This mode adds JST-310027 warning messages.
<urlPermission action="warn" debugTrace="true">
  <url dnsSuffix="" urlRegEx=".*" />
  <url dnsSuffix="" urlRegEx=".*" />
  <url dnsSuffix="" urlRegEx=".*" />

New clients will use the blocking mode. If they want to allow a new URL, they need to contact their administrator to add it to the allow list.

Existing customers coming from a migration can use the warning mode for a while. Meanwhile they need to analyze the outbound trafic before authorizing the URLS.

Command restriction (server-side)

Several commands are blacklisted and cannot be executed using the execCommand function. An extra-security is provided by a dedicated Unix user to execute external commands. For hosted installations, this restriction is automatically applied. For on-premise installations, you can manually set up this restriction by following the dedicated documentation. In addition, Script and External task workflow activities are not available (newly installed instances).

Other configurations

You can add extra HTTP headers (for all pages): refer to the documentation:

  • You can add some additional headers such as HSTS, X-FRAME-OPTIONS, CSP...
  • You have to test them in a test environment before applying them in production. WARNING: Adobe Campaign can be broken by adding certain headers.

Adobe Campaign lets you set a plain password in the <dbcnx .../> element. Do not use this feature.

By default, Adobe Campaign does not stick a session to a specific IP, but you can active it to prevent the session from being stolen. To do it, in the serverConf.xml file, set the checkIPConsistent attribute to true in the <authentication> node.

By default, Adobe Campaign's MTA does not use a secured connection to send content to the SMTP server. You have to enable this feature (may reduce delivery speed). To do this, set enableTLS to true in the <smtp ...> node.

You can reduce the lifetime of a session in the authentication node (sessionTimeOutSec attribute).

Web-server configuration


You will find below some of the main best practices related to web-server Apache/IIS) configuration.

Change default error pages.

Disable old SSL version and ciphers:

  • on Apache, edit /etc/apache2/mods-available/ssl.conf. Here is an example:
    • SSLProtocol all -SSLv2 -SSLv3 -TLSv1
    • SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!SSLv3:!SSLv2:!TLSv1
  • on IIS (see the documentation), perform the following configuration:
    • Add registry subkey in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL
    • To enable the system to use the protocols that will not be negotiated by default (such as TLS 1.2), change the DWORD value data of the DisabledByDefault value to 0x0 in the following registry keys under the Protocols key:

      SCHANNEL\Protocols\TLS 1.2\Client

      SCHANNEL\Protocols\TLS 1.2\Server

    • Disable SSL x.0:

      SCHANNEL\Protocols\SSL 3.0\Client: DisabledByDefault: DWORD (32-bit) Value to 1

      SCHANNEL\Protocols\SSL 3.0\Server: Enabled: DWORD (32-bit) Value to 0

Remove the TRACE method:

  • on Apache, edit in /etc/apache2/conf.d/security: TraceEnable Off
  • on IIS (see the documentation), perform the following configuration:
    • Make sure that Request Filtering role service or feature is installed.
    • In the Request Filtering pane, click the HTTP verbs tab, and then click Deny Verb. In the Actions pane, enter TRACE in the open dialog.

Remove the banner:

  • on Apache, edit /etc/apache2/conf.d/security:
    • ServerSignature Off
    • ServerTokens Prod
  • on IIS (see the documentation), perform the following configuration:
    • Install URLScan.
    • Edit the Urlscan.ini file to have RemoveServerHeader=1

Limit query size to prevent important files from being uploaded:

  • On Apache (see the documentation), add the LimitRequestBody directive (size in bytes) in / directory.

<Directory />
        Options FollowSymLinks
        AllowOverride None
        LimitRequestBody 10485760
  • On IIS (see the documentation), set the maxAllowedContentLength (maximum allowed content length) in the content filtering options.