diff --git a/doc/src/sgml/ref/pg_rewind.sgml b/doc/src/sgml/ref/pg_rewind.sgml
index 520d843f0e..0952f3903e 100644
--- a/doc/src/sgml/ref/pg_rewind.sgml
+++ b/doc/src/sgml/ref/pg_rewind.sgml
@@ -95,6 +95,25 @@ PostgreSQL documentation
    are currently on by default.  <xref linkend="guc-full-page-writes"/>
    must also be set to <literal>on</literal>, but is enabled by default.
   </para>
+
+  <para>
+   Before rewriting any files on the target data directory,
+   <application>pg_rewind</application> checks if any files are writable,
+   failing immediately if that is not the case.  It is possible to
+   execute again a rewind after fixing the permission issues.  This can
+   happen for example for read-only SSL-related configuration files.
+   Note that if the file is copied from the source to the target, then
+   it may be necessary to rebuild the links properly as a rewind would
+   have caused those files to be created with newer permission sets.
+  </para>
+
+  <warning>
+   <para>
+    If <application>pg_rewind</application> fails while processing, then
+    the data folder of the target is likely not in a state that can be
+    recovered.  In such a case, taking a new fresh backup is recommended.
+   </para>
+  </warning>
  </refsect1>
 
  <refsect1>
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
index c3fc519895..c0b58c76ce 100644
--- a/src/bin/pg_rewind/filemap.c
+++ b/src/bin/pg_rewind/filemap.c
@@ -189,7 +189,56 @@ process_source_file(const char *path, file_type_t type, size_t newsize,
 		exists = false;
 	}
 	else
-		exists = true;
+	{
+		int			fd;
+
+		/*
+		 * The file exists on the source and the target.  Now check if
+		 * the file is writable.
+		 */
+		fd = open(localpath, O_WRONLY);
+
+		if (fd < 0)
+		{
+			if (errno == EISDIR)
+			{
+				/*
+				 * No need to worry about directories, those are not
+				 * valid targets for writes.
+				 */
+				exists = true;
+			}
+			else if (errno == EACCES)
+			{
+				/*
+				 * The file exists on the source and the target but it is
+				 * not writable on target.  In order to avoid failures
+				 * mid-flight when processing the data directory, fail
+				 * immediately and let the user know.  This way, if anything
+				 * like a read-only file is found, then pg_rewind complains
+				 * and a successive run can be completed after doing some
+				 * cleanup on this target data directory.
+				 */
+				pg_fatal("file \"%s\" exists on both target and source server, but is not writable on target.\n"
+						 "Please check the permissions on \"%s\" before executing again pg_rewind.\n",
+						 path, localpath);
+			}
+			else
+			{
+				/*
+				 * Complain about anything else, that should not happen,
+				 * even ENOENT which has already been checked above.
+				 */
+				pg_fatal("could not open file \"%s\": %s\n",
+						 localpath, strerror(errno));
+			}
+		}
+		else
+		{
+			exists = true;
+			close(fd);
+		}
+	}
 
 	switch (type)
 	{
diff --git a/src/bin/pg_rewind/t/006_readonly.pl b/src/bin/pg_rewind/t/006_readonly.pl
new file mode 100644
index 0000000000..489e1f4593
--- /dev/null
+++ b/src/bin/pg_rewind/t/006_readonly.pl
@@ -0,0 +1,77 @@
+# Test how pg_rewind reacts to read-only files in the data dirs.
+# All such files should be ignored in the process.
+
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 2;
+
+use File::Copy;
+use File::Find;
+
+use RewindTest;
+
+my $test_mode = "remote";
+
+RewindTest::setup_cluster($test_mode);
+RewindTest::start_master();
+RewindTest::create_standby($test_mode);
+
+# Create the same read-only file in standby and master
+my $test_master_datadir = $node_master->data_dir;
+my $test_standby_datadir = $node_standby->data_dir;
+my $readonly_master = "$test_master_datadir/readonly_file";
+my $readonly_standby = "$test_standby_datadir/readonly_file";
+
+append_to_file($readonly_master, "in master");
+append_to_file($readonly_standby, "in standby");
+chmod 0400, $readonly_master, $readonly_standby;
+
+RewindTest::promote_standby();
+
+# Stop the master and run pg_rewind.
+$node_master->stop;
+
+my $master_pgdata   = $node_master->data_dir;
+my $standby_pgdata  = $node_standby->data_dir;
+my $standby_connstr = $node_standby->connstr('postgres');
+my $tmp_folder      = TestLib::tempdir;
+
+# Keep a temporary postgresql.conf for master node or it would be
+# overwritten during the rewind.
+copy(
+	"$master_pgdata/postgresql.conf",
+	"$tmp_folder/master-postgresql.conf.tmp");
+
+# Including read-only data in the source and the target will
+# cause pg_rewind to fail.
+my $rewind_command = [   'pg_rewind',       "--debug",
+		"--source-server", $standby_connstr,
+		"--target-pgdata=$master_pgdata" ];
+
+command_fails($rewind_command, 'pg_rewind fails with read-only');
+
+# Now remove the read-only data on both sides, the data folder
+# from the previous attempt should still be able to work.
+unlink($readonly_master);
+unlink($readonly_standby);
+command_ok($rewind_command, 'pg_rewind passes without read-only');
+
+# Now move back postgresql.conf with old settings
+move("$tmp_folder/master-postgresql.conf.tmp",
+	 "$master_pgdata/postgresql.conf");
+
+# Plug-in rewound node to the now-promoted standby node
+my $port_standby = $node_standby->port;
+$node_master->append_conf('recovery.conf', qq(
+primary_conninfo='port=$port_standby'
+standby_mode=on
+recovery_target_timeline='latest'
+));
+
+# Restart the master to check that rewind went correctly.
+$node_master->start;
+
+RewindTest::clean_rewind_test();
+
+exit(0);
