diff --git a/BUILD b/BUILD
index ce324887e329e4790f6184f4474b718595d9816a..86725b6f1cbcf439bbad35585be74a2bb3f9c63a 100644
--- a/BUILD
+++ b/BUILD
@@ -1224,6 +1224,7 @@ cc_library(
     "src/cpp/common/secure_auth_context.h",
     "src/cpp/server/secure_server_credentials.h",
     "src/cpp/client/create_channel_internal.h",
+    "src/cpp/common/channel_filter.h",
     "src/cpp/server/dynamic_thread_pool.h",
     "src/cpp/server/thread_pool_interface.h",
     "src/cpp/client/secure_credentials.cc",
@@ -1264,7 +1265,6 @@ cc_library(
   hdrs = [
     "include/grpc++/alarm.h",
     "include/grpc++/channel.h",
-    "include/grpc++/channel_filter.h",
     "include/grpc++/client_context.h",
     "include/grpc++/completion_queue.h",
     "include/grpc++/create_channel.h",
@@ -1457,6 +1457,7 @@ cc_library(
   name = "grpc++_unsecure",
   srcs = [
     "src/cpp/client/create_channel_internal.h",
+    "src/cpp/common/channel_filter.h",
     "src/cpp/server/dynamic_thread_pool.h",
     "src/cpp/server/thread_pool_interface.h",
     "src/cpp/common/insecure_create_auth_context.cc",
@@ -1492,7 +1493,6 @@ cc_library(
   hdrs = [
     "include/grpc++/alarm.h",
     "include/grpc++/channel.h",
-    "include/grpc++/channel_filter.h",
     "include/grpc++/client_context.h",
     "include/grpc++/completion_queue.h",
     "include/grpc++/create_channel.h",
diff --git a/Makefile b/Makefile
index 1a4a303ad3e335939d1339f394d9bda443640a7e..d2c263cc976262e5420168b0c71e09cd499a37e3 100644
--- a/Makefile
+++ b/Makefile
@@ -1489,6 +1489,7 @@ buildtests_cxx: buildtests_zookeeper privatelibs_cxx \
   $(BINDIR)/$(CONFIG)/cxx_string_ref_test \
   $(BINDIR)/$(CONFIG)/cxx_time_test \
   $(BINDIR)/$(CONFIG)/end2end_test \
+  $(BINDIR)/$(CONFIG)/filter_end2end_test \
   $(BINDIR)/$(CONFIG)/generic_end2end_test \
   $(BINDIR)/$(CONFIG)/golden_file_test \
   $(BINDIR)/$(CONFIG)/grpc_cli \
@@ -3497,7 +3498,6 @@ LIBGRPC++_SRC = \
 PUBLIC_HEADERS_CXX += \
     include/grpc++/alarm.h \
     include/grpc++/channel.h \
-    include/grpc++/channel_filter.h \
     include/grpc++/client_context.h \
     include/grpc++/completion_queue.h \
     include/grpc++/create_channel.h \
@@ -3986,7 +3986,6 @@ LIBGRPC++_UNSECURE_SRC = \
 PUBLIC_HEADERS_CXX += \
     include/grpc++/alarm.h \
     include/grpc++/channel.h \
-    include/grpc++/channel_filter.h \
     include/grpc++/client_context.h \
     include/grpc++/completion_queue.h \
     include/grpc++/create_channel.h \
diff --git a/build.yaml b/build.yaml
index b1a11d503d6eb09db15cb9296e7846cd64982e3d..0a78e52775cf5f377cfefb610665009e34d74956 100644
--- a/build.yaml
+++ b/build.yaml
@@ -638,7 +638,6 @@ filegroups:
   public_headers:
   - include/grpc++/alarm.h
   - include/grpc++/channel.h
-  - include/grpc++/channel_filter.h
   - include/grpc++/client_context.h
   - include/grpc++/completion_queue.h
   - include/grpc++/create_channel.h
@@ -686,6 +685,7 @@ filegroups:
   - include/grpc++/support/time.h
   headers:
   - src/cpp/client/create_channel_internal.h
+  - src/cpp/common/channel_filter.h
   - src/cpp/server/dynamic_thread_pool.h
   - src/cpp/server/thread_pool_interface.h
   src:
diff --git a/src/cpp/common/channel_filter.cc b/src/cpp/common/channel_filter.cc
index ab43b8ac3c834628e0ea2ce13de2dc89c9a9063d..8a4149bbcae5fcdb1254120196ba83934dd6c44b 100644
--- a/src/cpp/common/channel_filter.cc
+++ b/src/cpp/common/channel_filter.cc
@@ -31,11 +31,10 @@
  *
  */
 
-#include <grpc++/channel_filter.h>
-
 #include <string.h>
 
 #include "src/core/lib/channel/channel_stack.h"
+#include "src/cpp/common/channel_filter.h"
 
 namespace grpc {
 
diff --git a/include/grpc++/channel_filter.h b/src/cpp/common/channel_filter.h
similarity index 98%
rename from include/grpc++/channel_filter.h
rename to src/cpp/common/channel_filter.h
index e0703076551f119bf575771c4059b6548f937b50..437c7a2759528541a333f4828f9dc834f78d2c72 100644
--- a/include/grpc++/channel_filter.h
+++ b/src/cpp/common/channel_filter.h
@@ -122,7 +122,7 @@ class TransportOp {
 
   grpc_transport_op *op() const { return op_; }
 
-  // FIXME: add a C++ wrapper for grpc_error?
+  // TODO(roth): Add a C++ wrapper for grpc_error?
   grpc_error *disconnect_with_error() const {
     return op_->disconnect_with_error;
   }
@@ -211,8 +211,7 @@ class ChannelData {
 
   const char *peer() const { return peer_; }
 
-  // FIXME: find a way to avoid passing elem into these methods
-  // (same for CallData below)
+  // TODO(roth): Find a way to avoid passing elem into these methods.
   virtual void StartTransportOp(grpc_exec_ctx *exec_ctx,
                                 grpc_channel_element *elem, TransportOp *op);
 
@@ -230,6 +229,8 @@ class CallData {
 
   virtual grpc_error *Init() { return GRPC_ERROR_NONE; }
 
+  // TODO(roth): Find a way to avoid passing elem into these methods.
+
   virtual void StartTransportStreamOp(grpc_exec_ctx *exec_ctx,
                                       grpc_call_element *elem,
                                       TransportStreamOp *op);
diff --git a/test/cpp/end2end/filter_end2end_test.cc b/test/cpp/end2end/filter_end2end_test.cc
index dcaca10c7f40e874420ef90f6dd1b278ce98fbf9..576d440c9bda49c810630caffbb3a9cf0b38a8d1 100644
--- a/test/cpp/end2end/filter_end2end_test.cc
+++ b/test/cpp/end2end/filter_end2end_test.cc
@@ -35,7 +35,6 @@
 #include <mutex>
 
 #include <grpc++/channel.h>
-#include <grpc++/channel_filter.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
 #include <grpc++/generic/async_generic_service.h>
@@ -50,6 +49,7 @@
 #include <grpc/support/time.h>
 #include <gtest/gtest.h>
 
+#include "src/cpp/common/channel_filter.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index e770574cb1f4ceb31a14561fc227756cc2e31de6..7f9d2df6f6c026aacc53181ecf8ddbca6a61d65f 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -762,7 +762,6 @@ WARN_LOGFILE           =
 
 INPUT                  = include/grpc++/alarm.h \
 include/grpc++/channel.h \
-include/grpc++/channel_filter.h \
 include/grpc++/client_context.h \
 include/grpc++/completion_queue.h \
 include/grpc++/create_channel.h \
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index a3c4a109264c404cb503353c76c4337ec5fb30be..3c81c48d4a0bc4c5970893daf1a4be33f20e0ea8 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -762,7 +762,6 @@ WARN_LOGFILE           =
 
 INPUT                  = include/grpc++/alarm.h \
 include/grpc++/channel.h \
-include/grpc++/channel_filter.h \
 include/grpc++/client_context.h \
 include/grpc++/completion_queue.h \
 include/grpc++/create_channel.h \
@@ -864,6 +863,7 @@ src/cpp/client/secure_credentials.h \
 src/cpp/common/secure_auth_context.h \
 src/cpp/server/secure_server_credentials.h \
 src/cpp/client/create_channel_internal.h \
+src/cpp/common/channel_filter.h \
 src/cpp/server/dynamic_thread_pool.h \
 src/cpp/server/thread_pool_interface.h \
 src/cpp/client/secure_credentials.cc \
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 7d16d15eb454b5dea62099dd75c8bff57fab477b..e55d8a0997d3af096ed3fa8ad383f9af5e49c9a5 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -6537,7 +6537,6 @@
     "headers": [
       "include/grpc++/alarm.h", 
       "include/grpc++/channel.h", 
-      "include/grpc++/channel_filter.h", 
       "include/grpc++/client_context.h", 
       "include/grpc++/completion_queue.h", 
       "include/grpc++/create_channel.h", 
@@ -6584,6 +6583,7 @@
       "include/grpc++/support/sync_stream.h", 
       "include/grpc++/support/time.h", 
       "src/cpp/client/create_channel_internal.h", 
+      "src/cpp/common/channel_filter.h", 
       "src/cpp/server/dynamic_thread_pool.h", 
       "src/cpp/server/thread_pool_interface.h"
     ], 
@@ -6592,7 +6592,6 @@
     "src": [
       "include/grpc++/alarm.h", 
       "include/grpc++/channel.h", 
-      "include/grpc++/channel_filter.h", 
       "include/grpc++/client_context.h", 
       "include/grpc++/completion_queue.h", 
       "include/grpc++/create_channel.h", 
@@ -6649,6 +6648,7 @@
       "src/cpp/client/insecure_credentials.cc", 
       "src/cpp/common/channel_arguments.cc", 
       "src/cpp/common/channel_filter.cc", 
+      "src/cpp/common/channel_filter.h", 
       "src/cpp/common/completion_queue.cc", 
       "src/cpp/common/core_codegen.cc", 
       "src/cpp/common/rpc_method.cc", 
diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj b/vsprojects/vcxproj/grpc++/grpc++.vcxproj
index b882c302bbb225de0063d103318c312361a8ad5a..835e2527c995fd342df1aa95b004d64a4a4f7d49 100644
--- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj
+++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj
@@ -260,7 +260,6 @@
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\alarm.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\channel.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc++\channel_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\client_context.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\completion_queue.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\create_channel.h" />
@@ -364,6 +363,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\cpp\common\secure_auth_context.h" />
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\secure_server_credentials.h" />
     <ClInclude Include="$(SolutionDir)\..\src\cpp\client\create_channel_internal.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\cpp\common\channel_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\dynamic_thread_pool.h" />
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\thread_pool_interface.h" />
   </ItemGroup>
diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
index 08fffb74b2fb18ed6762edd7e563f0d322d0430d..883e66e1dfd466146322e43b2498523c737034c9 100644
--- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
@@ -111,9 +111,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\channel.h">
       <Filter>include\grpc++</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc++\channel_filter.h">
-      <Filter>include\grpc++</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\client_context.h">
       <Filter>include\grpc++</Filter>
     </ClInclude>
@@ -419,6 +416,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\cpp\client\create_channel_internal.h">
       <Filter>src\cpp\client</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\cpp\common\channel_filter.h">
+      <Filter>src\cpp\common</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\dynamic_thread_pool.h">
       <Filter>src\cpp\server</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
index b5a27f624d12c578ef0d94fe7c6ac1ba5ee2ce6a..e71180feb058650e95c5ec6d5220b90235d9a3fa 100644
--- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
@@ -260,7 +260,6 @@
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\alarm.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\channel.h" />
-    <ClInclude Include="$(SolutionDir)\..\include\grpc++\channel_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\client_context.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\completion_queue.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\create_channel.h" />
@@ -360,6 +359,7 @@
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(SolutionDir)\..\src\cpp\client\create_channel_internal.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\cpp\common\channel_filter.h" />
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\dynamic_thread_pool.h" />
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\thread_pool_interface.h" />
   </ItemGroup>
diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
index 68d9a47973dac68ce26c9a7bf320cf07fe7d25f2..a9aa147e56dee218935d46219e54ae86c9f417c2 100644
--- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
@@ -96,9 +96,6 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\channel.h">
       <Filter>include\grpc++</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\include\grpc++\channel_filter.h">
-      <Filter>include\grpc++</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\client_context.h">
       <Filter>include\grpc++</Filter>
     </ClInclude>
@@ -392,6 +389,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\cpp\client\create_channel_internal.h">
       <Filter>src\cpp\client</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\cpp\common\channel_filter.h">
+      <Filter>src\cpp\common</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\cpp\server\dynamic_thread_pool.h">
       <Filter>src\cpp\server</Filter>
     </ClInclude>